本文描述了如何将一个lagom工程部署到k8s,分如下几个部分讲述:
Lagom framework是一个集成ES/CQRS的微服务框架,但是真正使用的时候不一定非要用CQRS,个人觉得CQRS适合在高并发维护状态的业务场景下使用。普通情况下的CRUD集成Slick或者Quill就行。
使用Lagom framework,能够快速的构建一个多微服务工程。微服务之间可以通过服务发现,方便的进行交互。借助akka cluster,每个微服务在k8s上可以方便的水平扩展。
出于这些优势,企业或者一个开发组,可以把内部所有工程当做一个lagom工程的子工程,更可以抽象出一些通用的模块,然后把工程集成在一起,方便管理,更方便服务间的交互。
目前我了解到可以使用两种方式实现微服务之间的交互:
下载官方样例工程,我使用的最新版本是1.6.x,选在内部的shopping-cart-scala工程。
本文不使用官方提供的shell来部署发布,kafka和postgre使用已有的本地环境,一般kafka和数据库不发布在k8s上,这样更切合于线上环境情形。
IntelliJ IDEA引入工程,下载相关的依赖包,建议使用华为sbt repository。
[repositories]
local
huaweicloud-ivy: https://mirrors.huaweicloud.com/repository/ivy/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
huaweicloud-maven: https://mirrors.huaweicloud.com/repository/maven/
aliyun-ivy: http://maven.aliyun.com/nexus/content/groups/public, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]
aliyun-maven: http://maven.aliyun.com/nexus/content/groups/public
akka-nightly: http://repo.akka.io/snapshots/
maven-local
在inventory和shopping-cart的prod-application.conf上增加kafka配置:
lagom.broker.kafka {
# The name of the Kafka service to look up out of the service locator.
# If this is an empty string, then a service locator lookup will not be done,
# and the brokers configuration will be used instead.
service-name = ""
# The URLs of the Kafka brokers. Separate each URL with a comma.
# This will be ignored if the service-name configuration is non empty.
brokers = ${KAFKA_BROKERS}
}
如果是本地idea运行,只需要关注application.conf,在build.sbt上增加设置kafka,例如:lagomKafkaAddress in ThisBuild := "cs188:9099"
Sbt提供了方便的组件来制作docker镜像,不需要自己编写dockerfile,可以查看build.sbt中的dockerSettings信息。
工程目录下执行如下命令:
sbt docker:publishLocal
特别的,如果你不想手工制作忽略下面内容,想手动制作参考如下:shopping-cart有两个微服务,需要制作两个镜像,下面以inventory为例:
编写dockerfile,jdk根据自己环境修改
FROM java:oracle-java-8-jdk
MAINTAINER "lookqlp"
WORKDIR /test
EXPOSE 8080
COPY inventory/target/universal/inventory-0.1.0-SNAPSHOT.zip /test/inv.zip
RUN unzip inv.zip && rm -f inv.zip
CMD java -Dhttp.port=8080 -cp /test/inventory-0.1.0-SNAPSHOT/conf/:/test/inventory-0.1.0-SNAPSHOT/lib/* play.core.server.ProdServerStart
修改project/plugins.sbt,注释掉dynver、scalafmt,不然打的zip包含有时间后缀。
addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.6.1")
// Set the version dynamically to the git hash
//addSbtPlugin("com.dwijnand" % "sbt-dynver" % "3.3.0")
//
//addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.2.0")
执行,根据需要修改路径、镜像名称和版本号
docker build -f path/dockerfile -t inventory:v1 .
如果k8s环境不是本机,需要将镜像存到私有的docker仓库中,下面有Docker相关命令说明
//上传到指定docker仓库
docker tag inventory:v1 myhub.xxx.com/inventory:v1
docker push myhub.xxx.com/inventory:v1
//下载镜像
docker pull myhub.xxx.com/inventory:v1
//登录
docker login myhub.xxx.com
inventory.yaml:
apiVersion: "apps/v1"
kind: Deployment
metadata:
name: inventory
spec:
replicas: 1
selector:
matchLabels:
app: inventory
template:
metadata:
labels:
app: inventory
spec:
containers:
- name: inventory
image: "inventory:0.1.0-SNAPSHOT"
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx256m -Dconfig.resource=prod-application.conf"
- name: APPLICATION_SECRET
value: "123445"
- name: KAFKA_BROKERS
value: "172.18.21.188:9099"
---
apiVersion: v1
kind: Service
metadata:
name: inventory
spec:
ports:
- name: http
port: 8081
targetPort: 9000
selector:
app: inventory
type: LoadBalancer
说明:
shopping-cart.yaml:
apiVersion: "apps/v1"
kind: Deployment
metadata:
name: shopping-cart
spec:
replicas: 3
selector:
matchLabels:
app: shopping-cart
template:
metadata:
labels:
app: shopping-cart
spec:
containers:
- name: shopping-cart
image: "shopping-cart:0.1.0-SNAPSHOT"
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx256m -Dconfig.resource=prod-application.conf"
- name: APPLICATION_SECRET
value: "123456"
- name: POSTGRESQL_URL
value: "jdbc:postgresql://172.18.21.188/shopping_cart"
- name: POSTGRESQL_USERNAME
value: shopping_cart
- name: POSTGRESQL_PASSWORD
value: shopping_cart
- name: KAFKA_BROKERS
value: "172.18.21.188:9099"
- name: REQUIRED_CONTACT_POINT_NR
value: "3"
ports:
- name: management
containerPort: 8558
readinessProbe:
httpGet:
path: "/ready"
port: management
periodSeconds: 10
failureThreshold: 10
initialDelaySeconds: 20
livenessProbe:
httpGet:
path: "/alive"
port: management
periodSeconds: 10
failureThreshold: 10
initialDelaySeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: shopping-cart
spec:
ports:
- name: http
port: 8080
targetPort: 9000
nodePort: 31234
selector:
app: shopping-cart
type: LoadBalancer
同上,说明:
如果想发布到指定的namespace,参考:
// my.yaml
{
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {
"name": "shopping-cart",
"labels": {
"name": "shopping-cart"
}
}
}
// 新建
kubectl apply -f my.yaml
// 切换当前空间
kubectl config set-context --current --namespace=shopping-cart
//新建角色,rbac参考[openshift](https://developer.lightbend.com/guides/openshift-deployment/lagom/forming-a-cluster.html)
kubectl apply -f shopping-cart/deploy/specs/common/rbac.yaml
//运行,注意需要在正确的namespace下执行
//两个服务发布在相同的namespace下,不然服务间不能正常交互,报错:Service shopping-cart was not found by service locator
//可以使用-n shopping-cart来指定
kubectl apply -f shopping-cart.yaml
kubectl apply -f inventory.yaml
//删除
kubectl delete -f shopping-cart.yaml
kubectl delete -f inventory.yaml
//查看网络
kubectl get svc
//查看指定pod日志
kubectl logs podid
//查看指定组件描述信息
kubectl describe pod podid
kubectl describe service serviceid
//查看所有
kubectl get all
k8s运行服务的列表:
NAME READY STATUS RESTARTS AGE
pod/inventory-64bdcd5cfc-rbslf 1/1 Running 0 17h
pod/shopping-cart-64468ccb66-7ffwk 1/1 Running 0 17h
pod/shopping-cart-64468ccb66-h7v2n 1/1 Running 0 17h
pod/shopping-cart-64468ccb66-jbtpw 1/1 Running 0 17h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/inventory LoadBalancer 10.110.98.10 localhost 8081:30441/TCP 17h
service/shopping-cart LoadBalancer 10.103.238.201 localhost 8080:31234/TCP 17h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/inventory 1/1 1 1 17h
deployment.apps/shopping-cart 4/4 4 4 17h
NAME DESIRED CURRENT READY AGE
replicaset.apps/inventory-64bdcd5cfc 1 1 1 17h
replicaset.apps/shopping-cart-64468ccb66 4 4 4 17h
可以看到很多组件,有pod、service、deployment、replicaset,具体含义简单了解可以看看这篇软文。
本地单节点k8s集群,端口使用配置中8080和8081,如果是正式的k8s集群,可以使用nodePort。
curl http://localhost:8080/shoppingcart/123
curl -H "Content-Type: application/json" -d '{"itemId": "456", "quantity": 2}' -X POST http://localhost:8080/shoppingcart/123
curl http://localhost:8081/inventory/456
因为部署的service是LoadBalancer模式,请求会发送到shopping-cart服务随机一个节点上。
inventory通过服务发现,并引用,可以在工程里直接使用shoppingCartService。
lazy val shoppingCartService = serviceClient.implement[ShoppingCartService]
message broker信息可以通过shoppingCartService.shoppingCartTopic.subscribe.atLeastOnce消费数据。
也可以通过如下方式直接调用shopping-cart接口
def reGet(itemId: String): ServiceCall[NotUsed, String] = ServiceCall {r=>
shoppingCartService.get(itemId).invoke().map{d=>
d.toString
}
}