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 ..."