源码:dubbo-demo/dubbo-demo-api
#这里只贴关键代码,其他在git下载。
#源码下载地址:”https://git.imooc.com/coding-335/mooc-k8s-demo”
#代码结构
#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
制作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
#直接删除start.sh下述红色圈的内容,从PIDS到fi。直接去掉这个,因为在docker中运行,也不会跟别的进程冲突,Docker中不需要检查这个。
#docker需要再次build、run、tag、push
改造方案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:服务发现策略
下一步要确定服务发现策略。
Dubbo的服务发现真是一个问题,有很多公司在用dubbo做docker的时候都会遇见类似的问题,就好像发现dubbo用各种服务发现的策略都不太合适,这里也是尝试了好多服务发现策略,最终还是采用host模式,相对来说是比较好的方式。如果使用桥接模式会有一个问题,因为有一个ZK,如果provider是运行在一个容器里的话,那么会有一个容器的IP,那么会把容器的IP-A取到,暴露给ZK,注册,那么consumer去读ZK地址的时候,就会拿A去访问provider,这里如果是K8S内的服务,肯定会没有问题,能访问到POD的IP,但是如果是集群外的服务,那么这个A是没法访问的,所以存在这样的问题。
用桥接的话也不是不行,可以使用一个环境变量,在容器启动的时候,在环境变量中指定所在宿主机的真实IP,但环境变量也不好弄,因为POD在启动的时候,也没法拿到所在宿主机的IP,所以只能通过文件扩展的方式,在宿主机上写一个文件/usr/env,把这个文件挂载到容器中,通过文件取到这个IP,这也是可以的,但比较麻烦,性能也没有host模式好。
如果使用host网络模式就没桥接模式那么麻烦,就相当于在主机上直接运行了一个provider,跟主机上运行的效果是一样的,直接把真实的IP写进去,没桥接的问题,但是host网络模式有一个问题,端口20880会直接监听到主机上,如果dubbo服务也调度在这里,也是20880,就会有端口冲突问题。所以要选用host网络模式,那么就必须保证每一个dubbo的端口都不一样,怎样保证呢?不可能让应用方去限定一个端口,避免更改的风险。
是否可以在K8S的层面去给其指定端口,在K8S配置中指定端口,启动的时候就可以用到这个端口,这样的话,把分散修改端口的权利集中在一起,管理K8S配置的人可以统一规划端口,甚至可以随机生成不同的端口,确实保证端口不会冲突。
比如在K8S配置中配置一个dubbo-port:20881的环境变量,启动的时候,端口就会变成20881,怎样实现呢?可以编写脚本,在脚本中可以取到这个变量,也可以改掉dubbo.properties中的配置,可以把配置中的端口改成20881。
#修改start.sh
#在”改造方案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
[root@node-1 zookeeper]# docker run --name zookeeper -p 2181:2181 --restart always -d hub.mooc.com/zookeeper/zookeeper:3.4.10
#停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