k8s部署canal-server使用configMap挂载方式报Read Only file System

k8s部署canal-server使用configMap挂载方式报Read Only file System

1.1、问题复现

由于部署canal-server时,需要修改主库master的数据库连接信息以及配置zookeeper,所以为了后续操作方便,于是使用了configMap挂载配置文件的方式,如下图所示:

 但是,在编写好deployment文件之后,部署时,发现在k8s容器中,不能部署成功,报错问题如下:

1.2、问题分析

看到上述报错,下意识的感觉应该是没有写操作权限,因为挂载的文件并没有覆盖掉镜像中指定路径下的文件,然后去dockerhub上找到镜像的文件,发现canal-server镜像创建了 admin:admin用户和用户组,如下图所示:

而当前k8s容器的用户和用户组为root:root,针对这种情况,网上也有类似的解决方案,说是要在配置的pod中添加相关的安全配置,指定用户和用户组进行操作,但是前提是需要知道对应的UID和GID,关于UID和GID,k8s中root用户默认的UID和GID都是0,当然,你也可以通过 cat /etc/passwd查看。关于cat /etc/passwd指令查询的用户信息解释,可以参看下面解释:

以root用户信息为例: root:​x:0:0:root:/root:/bin/bash共7个字段,并以 : 进行了分割,下表为字段从左到右含义依次说明

字段 说明
用户名 用户登录系统使用的用户名
密码 密码位
UID 用户标识号
GID 用户组标识号
注释性描述 例如:存放用户全名等信息
宿主目录 用户登录系统后的缺省目录
命令解释器 用户使用的shell命令,默认为bash

然后获取到对应的UID和GID之后,k8s官方文档中,针对用户不一致这种情况有对应的说明,通过设置pod容器对应的安全上下文操作,可以指定用户执行,如下所示:

 

官方文档链接如下:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/security-context/

然后看到这之后,于是便参照官方文档添加对应的配置

securityContext:
    runAsUser: 0
    runAsGroup: 0
    fsGroup: 0

修改完配置之后,满怀期待等待部署执行结果,后面发现这样操作,还是报上面的问题。于是感觉这种操作不对,然后就在github上去查看issues,找了好久,换各种搜索词,终于看到了这篇

然后,准备根据解决方法操作时,发现并没有对应的官方解决方案

虽然没有官方解决方案吧,但是看到了相关的解决思路,查看了app.sh文件的内容,发现与自己之前的猜想是一致的,原因找到了,解决方案又很令人头疼......

1.3、问题解决

关于configMap挂载文件这部分,如果是采用默认的k8s容器用户进行创建操作,挂载文件的用户和用户组都是root,如果修改挂载的路径可以以root用户挂载成功,这部分还是修改上面挂载路径时发现的。同时,在github上面看到了前辈留下的解决思路,又结合dockerfile中的文件执行命令,于是自己就准备通过覆盖app.sh文件内容,并修改掉当前/home/admin/app.sh文件中的授权操作,这部分结合当前使用configMap挂载文件操作。

说明操作说明也同步写在了github 3262 issue下,也可以查看https://github.com/alibaba/canal/issues/3262),如下图所示

1.3.1、添加configMap挂载配置

- mountPath: /home/admin/app.sh
  name: conf
  subPath: app.sh

1.3.2、添加app.sh文件内容

1、复制canal/docker/image/app.sh文件内容,修改如下图位置

 

 

2、全局搜索 su admin,将此替换为 su root,以容器默认的root用户权限操作

3、完整的app.sh文件内容如下:

#!/bin/bash
set -e

source /etc/profile
export JAVA_HOME=/usr/java/latest
export PATH=$JAVA_HOME/bin:$PATH
touch /tmp/start.log
# chown admin: /tmp/start.log
# chown -R admin: /home/admin/canal-server
host=`hostname -i`

# waitterm
#   wait TERM/INT signal.
#   see: http://veithen.github.io/2014/11/16/sigterm-propagation.html
waitterm() {
        local PID
        # any process to block
        tail -f /dev/null &
        PID="$!"
        # setup trap, could do nothing, or just kill the blocker
        trap "kill -TERM ${PID}" TERM INT
        # wait for signal, ignore wait exit code
        wait "${PID}" || true
        # clear trap
        trap - TERM INT
        # wait blocker, ignore blocker exit code
        wait "${PID}" 2>/dev/null || true
}

# waittermpid "${PIDFILE}".
#   monitor process by pidfile && wait TERM/INT signal.
#   if the process disappeared, return 1, means exit with ERROR.
#   if TERM or INT signal received, return 0, means OK to exit.
waittermpid() {
        local PIDFILE PID do_run error
        PIDFILE="${1?}"
        do_run=true
        error=0
        trap "do_run=false" TERM INT
        while "${do_run}" ; do
                PID="$(cat "${PIDFILE}")"
                if ! ps -p "${PID}" >/dev/null 2>&1 ; then
                        do_run=false
                        error=1
                else
                        sleep 1
                fi
        done
        trap - TERM INT
        return "${error}"
}


function checkStart() {
    local name=$1
    local cmd=$2
    local timeout=$3
    cost=5
    while [ $timeout -gt 0 ]; do
        ST=`eval $cmd`
        if [ "$ST" == "0" ]; then
            sleep 1
            let timeout=timeout-1
            let cost=cost+1
        elif [ "$ST" == "" ]; then
            sleep 1
            let timeout=timeout-1
            let cost=cost+1
        else
            break
        fi
    done
    echo "start $name successful"
}


function start_canal() {
    echo "start canal ..."
    managerAddress=`perl -le 'print $ENV{"canal.admin.manager"}'`
    if [ ! -z "$managerAddress" ] ; then
        # canal_local.properties mode
        adminPort=`perl -le 'print $ENV{"canal.admin.port"}'`
        if [ -z "$adminPort" ] ; then
            adminPort=11110
        fi

        su root -c 'cd /home/admin/canal-server/bin/ && sh restart.sh local 1>>/tmp/start.log 2>&1'
        sleep 5
        #check start
        checkStart "canal" "nc 127.0.0.1 $adminPort -w 1 -z | wc -l" 30
    else
        metricsPort=`perl -le 'print $ENV{"canal.metrics.pull.port"}'`
        if [ -z "$metricsPort" ] ; then
            metricsPort=11112
        fi

        destination=`perl -le 'print $ENV{"canal.destinations"}'`
        if [[ "$destination" =~ ',' ]]; then
            echo "multi destination:$destination is not support"
            exit 1;
        else
            if [ "$destination" != "" ] && [ "$destination" != "example" ] ; then
                if [ -d /home/admin/canal-server/conf/example ]; then
                    mv /home/admin/canal-server/conf/example /home/admin/canal-server/conf/$destination
                fi
            fi 
        fi

        su root -c 'cd /home/admin/canal-server/bin/ && sh restart.sh 1>>/tmp/start.log 2>&1'
        sleep 5
        #check start
        checkStart "canal" "nc 127.0.0.1 $metricsPort -w 1 -z | wc -l" 30
    fi  
}

function stop_canal() {
    echo "stop canal"
    su root -c 'cd /home/admin/canal-server/bin/ && sh stop.sh 1>>/tmp/start.log 2>&1'
    echo "stop canal successful ..."
}

function start_exporter() {
    su root -c 'cd /home/admin/node_exporter && ./node_exporter 1>>/tmp/start.log 2>&1 &'
}

function stop_exporter() {
    su root -c 'killall node_exporter'
}

echo "==> START ..."

start_exporter
start_canal

echo "==> START SUCCESSFUL ..."

tail -f /dev/null &
# wait TERM signal
waitterm

echo "==> STOP"

stop_canal
stop_exporter

echo "==> STOP SUCCESSFUL ..."
app.sh文件内容
posted @ 2022-09-30 21:42  星空流年  阅读(3495)  评论(0编辑  收藏  举报