YAZONG 我的开源

Kubernetes(七)(7.4/5/6)传统dubbo服务迁移kubernetes(上/下)

  , , ,
0 评论0 浏览

源码:dubbo-demo/dubbo-demo-api

#这里只贴关键代码,其他在git下载。

#源码下载地址:”https://git.imooc.com/coding-335/mooc-k8s-demo”

#代码结构

image.png

#start.sh

#!/bin/bash

cd `dirname $0`
BIN_DIR=`pwd`
cd ..

DEPLOY_DIR=`pwd`
CONF_DIR=${DEPLOY_DIR}/conf

SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
SERVER_PORT=`sed '/dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`

if [ -z "${SERVER_NAME}" ]; then
    echo "ERROR: can not found 'dubbo.application.name' config in 'dubbo.properties' !"
	exit 1
fi

if [ ! -z "${DUBBO_PORT}" ];then
        sed -i "s/dubbo.protocol.port=${SERVER_PORT}/dubbo.protocol.port=${DUBBO_PORT}/g" conf/dubbo.properties
        SERVER_PORT=${DUBBO_PORT}
fi

if [ -n "${SERVER_PORT}" ]; then
	SERVER_PORT_COUNT=`netstat -ntl | grep ${SERVER_PORT} | wc -l`
	if [ ${SERVER_PORT_COUNT} -gt 0 ]; then
		echo "ERROR: The ${SERVER_NAME} port ${SERVER_PORT} already used!"
		exit 1
	fi
fi

LOGS_DIR=""
if [ -n "${LOGS_FILE}" ]; then
	LOGS_DIR=`dirname ${LOGS_FILE}`
else
	LOGS_DIR=${DEPLOY_DIR}/logs
fi
if [ ! -d ${LOGS_DIR} ]; then
	mkdir ${LOGS_DIR}
fi
STDOUT_FILE=${LOGS_DIR}/stdout.log

LIB_DIR=${DEPLOY_DIR}/lib
LIB_JARS=`ls ${LIB_DIR} | grep .jar | awk '{print "'${LIB_DIR}'/"$0}'|tr "\n" ":"`

JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
JAVA_DEBUG_OPTS=""
if [ "$1" = "debug" ]; then
    JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
fi

echo -e "Starting the ${SERVER_NAME} ...\c"

${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main

PIDS=`ps  --no-heading -C java -f --width 1000 | grep "${DEPLOY_DIR}" | awk '{print $2}'`
echo "PID: ${PIDS}"
echo "STDOUT: ${STDOUT_FILE}"
#dubbo.properties

dubbo.application.name=demo
#这里是docker启动zookeeper后指定的部署节点node-1的实际地址
#zookeeper安装看”docker/k8s安装zookeeper”章节
dubbo.registry.address=zookeeper://172.16.1.21:2181
dubbo.spring.config=classpath*:spring/provider.xml
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

把服务做到镜像中

#第1步:构建镜像-DockerFile

[root@node-1 mooc-k8s-demo]# pwd
/root/mooc-k8s-demo
[root@node-1 mooc-k8s-demo]# mkdir dubbo-demo
[root@node-1 mooc-k8s-demo]# cd dubbo-demo
[root@node-1 dubbo-demo]# mkdir ROOT
[root@node-1 dubbo-demo]# mv dubbo-demo-1.0-SNAPSHOT-assembly.tar.gz ROOT/
[root@node-1 dubbo-demo]# cd ROOT/
#现在的文件是压缩包,怎么放入docker中呢,解压缩。
[root@node-1 ROOT]# tar -zxf dubbo-demo-1.0-SNAPSHOT-assembly.tar.gz 
[root@node-1 ROOT]# ll
total 11800
drwxrwxrwx 2 root root     4096 Nov 12 00:53 bin
drwxrwxrwx 3 root root     4096 Nov 12 00:53 conf
-rw-r--r-- 1 root root 12068092 Nov 12 00:54 dubbo-demo-1.0-SNAPSHOT-assembly.tar.gz
drwxr-xr-x 2 root root     4096 Nov 12 01:26 lib
[root@node-1 ROOT]# rm -f dubbo-demo-1.0-SNAPSHOT-assembly.tar.gz 
[root@node-1 ROOT]# cd ..
[root@node-1 dubbo-demo]# cat Dockerfile 
FROM hub.mooc.com/kubernetes/openjdk:8-jre-alpine
#这个ROOT 目录下才是真正的docker文件
COPY ROOT /ROOT3

ENTRYPOINT ["sh", "/ROOT3/bin/start.sh"]

[root@node-1 dubbo-demo]# docker build -t dubbo:v1 .
[root@node-1 dubbo-demo]# docker run -it dubbo:v1
Starting the demo ...[2022-11-11 17:37:47] Dubbo service server started!


[root@node-1 dubbo-demo]# docker tag dubbo:v1 hub.mooc.com/kubernetes/dubbo:v1
[root@node-1 dubbo-demo]# docker push hub.mooc.com/kubernetes/dubbo:v1
[root@node-1 dubbo-demo]# docker images|grep dubbo
dubbo                                    v1             df3431f207dd   9 minutes ago   98.6MB
hub.mooc.com/kubernetes/dubbo            v1             df3431f207dd   9 minutes ago   98.6MB

image.png

制作K8S服务并调度

#第1步:服务发现策略

这里先按步骤执行下去。服务发现策略参考:”改造方案4:服务发现策略”。

#第2步:编写K8S配置文件

#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-demo
spec:
  selector:
    matchLabels:
      app: dubbo-demo
  replicas: 1
  template:
    metadata:
      labels:
        app: dubbo-demo
    spec:
      hostNetwork: true
      nodeName: node-3
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - dubbo-demo
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: dubbo-demo
        image: hub.mooc.com/kubernetes/dubbo:v1
        ports:
        - containerPort: 20881
        #服务发现策略:动态修改开放端口。服务发现策略参考:”改造方案4:服务发现策略”。
        env:
        - name: DUBBO_PORT
          value: "20881"

#第3步:运行K8S服务

[root@node-1 configs]# pwd
/root/mooc-k8s-demo/configs
[root@node-1 configs]# kubectl apply -f dubbo.yaml
deployment.apps/dubbo-demo created

#查看进程
#注意这里是host网络模式
[root@node-1 configs]# kubectl get pods -o wide
NAME                           READY   STATUS    RESTARTS   AGE    IP               NODE     NOMINATED NODE   READINESS GATES
dubbo-demo-5fb89b885d-lwv2l    1/1     Running   0          3s     172.16.1.23      node-3   <none>           <none>
[root@node-1 configs]# kubectl logs dubbo-demo-5fb89b885d-lwv2l
Starting the demo ...[2022-11-11 18:38:08] Dubbo service server started!
[root@node-3 harbor]# crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                   ATTEMPT             POD ID
7ba8f3af36475       a5878a1e012c5       7 seconds ago       Running             dubbo-demo             0                   85d4ecd045c17
#查看部署节点端口
[root@node-3 harbor]# netstat -lntup|grep 20881
tcp        0      0 0.0.0.0:20881           0.0.0.0:*               LISTEN      92021/java 

#测试dubbo
[root@node-2 ~]# telnet 172.16.1.23 20881
Trying 172.16.1.23...
Connected to 172.16.1.23.
Escape character is '^]'.
#这里证明可以随时在配置文件中修改服务的端口。
dubbo>ls
com.mooc.demo.api.DemoService
dubbo>ls com.mooc.demo.api.DemoService
sayHello
dubbo>invoke com.mooc.demo.api.DemoService.sayHello("xxx")
"Hello xxx"
elapsed: 4 ms.
dubbo>exit
Connection closed by foreign host.

停服务(可选)

#停dubbo服务
[root@node-1 configs]# kubectl delete -f dubbo.yaml 
deployment.apps "dubbo-demo" deleted
[root@node-1 configs]# kubectl get pods -o wide
[root@node-3 harbor]# netstat -lntup|grep 20881
[root@node-3 harbor]# crictl ps
#停zookeeper服务
[root@node-1 zookeeper]# docker ps -a
CONTAINER ID   IMAGE                                     COMMAND                  CREATED        STATUS                     PORTS                                                           NAMES
df1193fd13a9   hub.mooc.com/zookeeper/zookeeper:3.4.10   "/docker-entrypoint.¡­"   2 hours ago    Up 2 hours                 2888/tcp, 0.0.0.0:2181->2181/tcp, :::2181->2181/tcp, 3888/tcp   zookeeper
[root@node-1 zookeeper]# docker stop df1193fd13a9
df1193fd13a9
[root@node-1 zookeeper]# docker rm d9b74af1ba65 
df1193fd13a9

解析:start.sh

#下述”改造方案”全要参考”未改造前start.sh”,从”改造方案1”依次往下执行到”改造方案4”。

未改造前start.sh

#!/bin/bash

cd `dirname $0`
BIN_DIR=`pwd`
cd ..

DEPLOY_DIR=`pwd`
CONF_DIR=${DEPLOY_DIR}/conf

SERVER_NAME=`sed '/dubbo.application.name/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`
SERVER_PORT=`sed '/dubbo.protocol.port/!d;s/.*=//' conf/dubbo.properties | tr -d '\r'`

if [ -z "${SERVER_NAME}" ]; then
    echo "ERROR: can not found 'dubbo.application.name' config in 'dubbo.properties' !"
	exit 1
fi

PIDS=`ps  --no-heading -C java -f --width 1000 | grep "${CONF_DIR}" |awk '{print $2}'`
if [ -n "${PIDS}" ]; then
    echo "ERROR: The ${SERVER_NAME} already started!"
    echo "PID: ${PIDS}"
    exit 1
fi

if [ -n "${SERVER_PORT}" ]; then
	SERVER_PORT_COUNT=`netstat -ntl | grep ${SERVER_PORT} | wc -l`
	if [ ${SERVER_PORT_COUNT} -gt 0 ]; then
		echo "ERROR: The ${SERVER_NAME} port ${SERVER_PORT} already used!"
		exit 1
	fi
fi

LOGS_DIR=""
if [ -n "${LOGS_FILE}" ]; then
	LOGS_DIR=`dirname ${LOGS_FILE}`
else
	LOGS_DIR=${DEPLOY_DIR}/logs
fi
if [ ! -d ${LOGS_DIR} ]; then
	mkdir ${LOGS_DIR}
fi
STDOUT_FILE=${LOGS_DIR}/stdout.log

LIB_DIR=${DEPLOY_DIR}/lib
LIB_JARS=`ls ${LIB_DIR} | grep .jar | awk '{print "'${LIB_DIR}'/"$0}'|tr "\n" ":"`

JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true "
JAVA_DEBUG_OPTS=""
if [ "$1" = "debug" ]; then
    JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
fi

echo -e "Starting the ${SERVER_NAME} ...\c"

nohup ${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main >> ${STDOUT_FILE} 2>&1 &

PIDS=`ps  --no-heading -C java -f --width 1000 | grep "${DEPLOY_DIR}" | awk '{print $2}'`
echo "PID: ${PIDS}"
echo "STDOUT: ${STDOUT_FILE}"

改造方案1:解决容器停止

[root@node-1 dubbo-demo]# pwd
/root/mooc-k8s-demo/dubbo-demo
[root@node-1 dubbo-demo]# cat Dockerfile 
FROM hub.mooc.com/kubernetes/openjdk:8-jre-alpine

COPY ROOT /ROOT3

ENTRYPOINT ["sh", "/ROOT3/bin/start.sh"]

#之前运行未改造前start.sh的时候直接输出两个就直接退出了。Docker中的ENTRYPOINT如果直接退出了,会发生什么呢?那么这个容器就停掉了,所以按照现阶段的启动方式是有问题的。以后台方式运行,就马上停止了,所以要改成前台运行。

#对照”未改造前start.sh”

#修改start.sh
nohup ${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main >> ${STDOUT_FILE} 2>&1 &
#变成(删除nohup和最后的&)
${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main >> ${STDOUT_FILE} 2>&1

#docker需要再次build、run、tag、push

#这里stop最终停止的方式是kill了一下。

#这个kill不加参数,默认给pid发送了一个15号的信号,即sigterm信号。

#Docker在K8S中的停止信号方式,也是给docker发送一个sigterm信号,就可以让其优雅退出,也就是说当程序挂起运行的时候,docker会给这个java进程发送sigtem信号,dubbo接到这个信号之后,会做一些收尾的工作,包括不再接收新的请求以及通知客户端它已经下线了,保证把现有的请求处理完,保证是一个优雅的退出,这个流程是dubbo内部自己实现的。现在要做的就是要给这个java发一个sigterm信号,刚好K8S也是这么做的,所以就不用考虑优雅退出的问题了。那么这里的stop.sh就不用修改。

改造方案2:删除no-heading

#对照”未改造前start.sh”

[root@node-1 dubbo-demo]# docker run -it dubbo:v1

image.png

#直接删除start.sh下述红色圈的内容,从PIDS到fi。直接去掉这个,因为在docker中运行,也不会跟别的进程冲突,Docker中不需要检查这个。

#docker需要再次build、run、tag、push

image.png

改造方案3:日志重定向

[root@node-1 dubbo-demo]# docker run -it dubbo:v1

Starting the demo ...

#为啥会一直停在这里呢????

#重定向日志里,在前台就看不到任何消息了。

#也可以这么保持不变,在docker中看日志文件就行了。

#当然也可以把这个重定向去掉,把这个所有的输出都在容器的标准输出中输出出来。

#修改start.sh
nohup ${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main >> ${STDOUT_FILE} 2>&1 &
#变成(删除nohup和最后的重定向)
${JAVA_HOME}/bin/java -Dapp.name=${SERVER_NAME} ${JAVA_OPTS} ${JAVA_DEBUG_OPTS} ${JAVA_JMX_OPTS} -classpath ${CONF_DIR}:${LIB_JARS} com.alibaba.dubbo.container.Main


#docker需要再次build、run、tag、push。
[root@node-1 dubbo-demo]# docker run -it dubbo:v1
#这样镜像就做好了。
Starting the demo ...[2022-11-11 17:37:47] Dubbo service server started!

改造方案4:服务发现策略

下一步要确定服务发现策略。

image.png

Dubbo的服务发现真是一个问题,有很多公司在用dubbo做docker的时候都会遇见类似的问题,就好像发现dubbo用各种服务发现的策略都不太合适,这里也是尝试了好多服务发现策略,最终还是采用host模式,相对来说是比较好的方式。如果使用桥接模式会有一个问题,因为有一个ZK,如果provider是运行在一个容器里的话,那么会有一个容器的IP,那么会把容器的IP-A取到,暴露给ZK,注册,那么consumer去读ZK地址的时候,就会拿A去访问provider,这里如果是K8S内的服务,肯定会没有问题,能访问到POD的IP,但是如果是集群外的服务,那么这个A是没法访问的,所以存在这样的问题。

image.png

用桥接的话也不是不行,可以使用一个环境变量,在容器启动的时候,在环境变量中指定所在宿主机的真实IP,但环境变量也不好弄,因为POD在启动的时候,也没法拿到所在宿主机的IP,所以只能通过文件扩展的方式,在宿主机上写一个文件/usr/env,把这个文件挂载到容器中,通过文件取到这个IP,这也是可以的,但比较麻烦,性能也没有host模式好。

image.png

如果使用host网络模式就没桥接模式那么麻烦,就相当于在主机上直接运行了一个provider,跟主机上运行的效果是一样的,直接把真实的IP写进去,没桥接的问题,但是host网络模式有一个问题,端口20880会直接监听到主机上,如果dubbo服务也调度在这里,也是20880,就会有端口冲突问题。所以要选用host网络模式,那么就必须保证每一个dubbo的端口都不一样,怎样保证呢?不可能让应用方去限定一个端口,避免更改的风险。

是否可以在K8S的层面去给其指定端口,在K8S配置中指定端口,启动的时候就可以用到这个端口,这样的话,把分散修改端口的权利集中在一起,管理K8S配置的人可以统一规划端口,甚至可以随机生成不同的端口,确实保证端口不会冲突。

比如在K8S配置中配置一个dubbo-port:20881的环境变量,启动的时候,端口就会变成20881,怎样实现呢?可以编写脚本,在脚本中可以取到这个变量,也可以改掉dubbo.properties中的配置,可以把配置中的端口改成20881。

#修改start.sh

image.png

#在”改造方案2”已删除红色内容的部分,变成下述内容。这样自定义的端口就到这了。

#docker需要再次build、run、tag、push。

if [ ! -z "${DUBBO_PORT}" ];then
        sed -i "s/dubbo.protocol.port=${SERVER_PORT}/dubbo.protocol.port=${DUBBO_PORT}/g" conf/dubbo.properties
        SERVER_PORT=${DUBBO_PORT}
fi

这个脚本就讲完了,支持了自定义的端口。

到这,服务发现策略也完成了。可以编写K8S配置了。

解析:dubbo.yaml

#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-demo
spec:
  selector:
    matchLabels:
      app: dubbo-demo
  replicas: 1
  template:
    metadata:
      labels:
        app: dubbo-demo
spec:
  #host网络模式
      hostNetwork: true
      #指定部署节点
      nodeName: node-3
      affinity:
        #亲和性调度,具体配置原理,后面详细讲解POD调度策略。这块配置的目的是,dubbo在多实例的情况下,不会调度在同一个节点上,从而产生端口冲突。
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - dubbo-demo
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: dubbo-demo
        image: hub.mooc.com/kubernetes/dubbo:v1
        ports:
        #固定指定启动端口,比如20880
        - containerPort: 20881
        #动态端口测试,要加入下述属性。如不需要不用加。
env:
        - name: DUBBO_PORT
          #端口值一定要和containerPort的值一致,这里一定是字符串格式!
          value: "20881"

docker安装zookeeper


[root@node-1 zookeeper]# pwd
/root/zookeeper

[root@node-1 zookeeper]# docker pull zookeeper:3.4.8
Error response from daemon: manifest for zookeeper:3.4.8 not found: manifest unknown: manifest unknown
#根据kubernetes学习案例的dubbo对应的zookeeper版本是3.4.8,可是拉取失败,这里拉取版本最近的3.4.10版本作为测试镜像。
[root@node-1 zookeeper]# docker pull zookeeper:3.4.10
[root@node-1 zookeeper]# docker tag zookeeper:3.4.10 hub.mooc.com/zookeeper/zookeeper:3.4.10
[root@node-1 zookeeper]# docker push hub.mooc.com/zookeeper/zookeeper:3.4.10

[root@node-1 zookeeper]# docker images|grep zookeeper
zookeeper                                3.4.10         d9fe1374256f   5 years ago     144MB
hub.mooc.com/zookeeper/zookeeper         3.4.10         d9fe1374256f   5 years ago     144MB

image.png

[root@node-1 zookeeper]# docker run --name zookeeper -p 2181:2181 --restart always -d hub.mooc.com/zookeeper/zookeeper:3.4.10

image.png

#停zookeeper服务
[root@node-1 zookeeper]# docker ps -a
CONTAINER ID   IMAGE                                     COMMAND                  CREATED        STATUS                     PORTS                                                           NAMES
df1193fd13a9   hub.mooc.com/zookeeper/zookeeper:3.4.10   "/docker-entrypoint.¡­"   2 hours ago    Up 2 hours                 2888/tcp, 0.0.0.0:2181->2181/tcp, :::2181->2181/tcp, 3888/tcp   zookeeper
[root@node-1 zookeeper]# docker stop df1193fd13a9
df1193fd13a9
[root@node-1 zookeeper]# docker rm d9b74af1ba65 
df1193fd13a9

标题:Kubernetes(七)(7.4/5/6)传统dubbo服务迁移kubernetes(上/下)
作者:yazong
地址:https://blog.llyweb.com/articles/2022/11/12/1668197593365.html