在之前的文章中我们配置了mysql和harbor,现在我们可以将一个springcloud部署在k8s集群中了。
项目概述
这个springcloud项目将采用maven进行打包部署。首先安装maven:
yum install java-1.8.0-openjdk maven-3.0.5* -y
然后将该项目上传到k8s集群的master节点,并解压。大概看一眼这个项目的文件目录结构,可以看到每一个功能都是分开建立的文件夹:
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
将我们安装数据库的地址写入文件中:
cat /root/microservic-test/stock-service/stock-service-biz/src/main/resources/application-fat.yml
spring:datasource:url: jdbc:mysql://192.168.244.128:3306/tb_stock?characterEncoding=utf-8username: rootpassword: rootdriver-class-name: com.mysql.jdbc.Drivereureka:instance:prefer-ip-address: trueclient:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
将这里的数据库信息修改为之前设置的信息。
需要修改三个地方,另外两个地方是:
cat /root/microservic-test/product-service/product-service-biz/src/main/resources/application-fat.yml
cat /root/microservic-test/order-service/order-service-biz/src/main/resources/ application-fat.yml
其实在每一个resource目录下有三个文件,分别是:application.yaml,application-fat.yaml和application-dev.yaml。查看application.yaml,可以看到这里规定了目前使用的profile是fat,也就是application-fat.yaml版本。这是我们只修改这个yaml文件的原因,另一个配置文件可能是用在别的环境中。
[root@master resources]# ls
application-dev.yml application-fat.yml application.yml
[root@master resources]# cat application.yml
server:port: 8030
spring:profiles:active: fatapplication:name: stock-service
maven构建
随后进行maven构建项目。 在microservice-test目录下执行:
mvn clean package -D maven.test.skip=true
显示如下则构建成功。
目录结构
在product service下,可以看到:
pom.xml product-service-api product-service-biz
pom.xml:这是 product-service 的父 pom.xml,用于管理子模块的依赖和构建。它可能包含项目的全局依赖、插件配置等。
product-service-api:这个模块用于定义服务的 API,比如接口、模型类、常量等。这个模块的目的是将服务的公共部分(如客户端调用时需要的接口)与业务逻辑分离。
product-service-biz:这个模块包含了主要的业务逻辑,例如控制器(controller)、数据访问层(mapper)、启动类(ProductServiceApp.java)等。这个模块通常是真正部署到服务器或容器中的服务部分。
product-service-api/
├── pom.xml
├── src
│ └── main
│ └── java
│ └── com
│ └── ctnrs
│ └── product
│ └── api
│ ├── client
│ │ └── ProductServiceClient.java
│ ├── constant
│ └── model
└── target
其中这个target是maven生成的,Maven 打包后,通常会生成一个打包文件(如 .jar
或 .war
),并且会放在target目录下。
product-service-biz/
├── Dockerfile
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── ctnrs
│ │ └── product
│ │ ├── controller
│ │ │ └── ProductController.java
│ │ ├── mapper
│ │ └── ProductServiceApp.java
│ └── resources
└── target
根据这个文件的部分目录结构,可以看到业务和公共部分逻辑的分离理念。
配置harbor
在之前的文章中,我们设置了harbor虚拟机对harbor服务的登录,现在将配置扩大到整个集群。在每一个节点上修改/etc/docker/daemon.json文件为下面的样子,并重新启动docker。
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors":["https://rsbud4vc.mirror.aliyuncs.com","https://registry.dockercn.com","https://docker.mirrors.ustc.edu.cn","https://dockerhub.azk8s.cn","http://hubmirror.c.163.com","http://qtid6917.mirror.aliyuncs.com"],
"insecure-registries":["192.168.244.131","harbor"],
"exec-opts":["native.cgroupdriver=systemd"],
"log-driver":"json-file",
"log-opts": {"max-size": "100m"},
"storage-driver":"overlay2" }
EOF
给登录habor配置一个secret。创建了一个namespace叫做ms。
kubectl create namespace ms && kubectl create secret docker-registry registry-pull-secret --docker-server=192.168.244.131 --docker-username=admin --docker-password=Harbor12345 -n ms
命令中的docker-registry是一种secret类型,用来存储用于访问私有 Docker 镜像仓库(如 Docker Hub、Harbor 等)的认证信息。包含Docker 注册表服务器地址(--docker-server),用户名(--docker-username),密码(--docker-password),可选的电子邮件地址(--docker-email)。
构建镜像
当我们用maven打包后,在每一个项目里面都会有一个target目录,存放.jar或.war文件。以eureka服务为例,进入目录后,看到Dockerfile文件和target目录。
[root@master microservic-test]# ls
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
[root@master microservic-test]# cd eureka-service/
[root@master eureka-service]# ls
Dockerfile pom.xml src target
[root@master eureka-service]# cat docker
cat: docker: 没有那个文件或目录
[root@master eureka-service]# cat Dockerfile
FROM java:8-jdk-alpine
RUN apk add -U tzdata && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/eureka-service.jar ./
EXPOSE 8888
CMD java -jar -Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms /eureka-service.jar
FROM java:8-jdk-alpine:这是基础镜像,基于 Alpine Linux 的 Java 8 JDK 镜像alpine 是一个轻量级的 Linux 发行版,适合构建小体积的 Docker 镜像。
- RUN apk add -U tzdata && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
- 通过apk包管理器安装tzdata,这是一个用于配置时区数据的包。
- 创建符号链接,将时区设置为中国标准时间Asia Shanghai。
- COPY ./target/eureka-service.jar ./:这行命令将本地路径target目录下的eureka-service.jar 文件复制到 Docker 容器的当前目录(
./
),即容器的根文件夹。Expose 8888:表示容器会监听 8888 端口,这是 Eureka 服务的默认端口之一(不过可以根据配置来修改)。
CMD java -jar -Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms /eureka-service.jar 这是一个运行命令。
假设你在 Kubernetes 中部署了一个 Pod,Pod 的名字是eureka-1,环境变量MY_POD_NAME就会是eureka-1。因此,最终的-Deureka.instance.host参数会变成:-Deureka.instance.hostname=eureka-1.eureka.ms,这方便eureka服务器找到这个pod。这里的ms是命名空间。
这一行规定了如何启动这个镜像:-jar -Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms /eureka-service.jar
因此,通过docker build生成镜像后,在部署这个镜像的yaml文件中需要传入启动参数,则这个服务可以运行。下面为一个举例说明。
apiVersion: apps/v1
kind: Deployment
metadata:name: eureka-servicelabels:app: eureka
spec:replicas: 1selector:matchLabels:app: eurekatemplate:metadata:labels:app: eurekaspec:containers:- name: eureka-serviceimage: <your-docker-registry>/eureka-service:latestports:- containerPort: 8888env:- name: MY_POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.namecommand: ["java"]args: ["-jar", "-Deureka.instance.hostname=${MY_POD_NAME}.eureka.ms", "/eureka-service.jar"]
通过查看target目录,可以看到确实有一个jar包。
[root@master eureka-service]# cd target
[root@master target]# ls
classes eureka-service.jar eureka-service.jar.original generated-sources maven-archiver maven-status
创建镜像项目
在之前创建的harbor的网页上新建一个microservice的项目,用于存储需要的镜像。
部署eureka项目
构建eureka镜像
在这里我发现我无法拉取dockerfile中写的java:8-jdk-alpine,所以我下载了openjdk:8-jdk-alpine,并且修改了dockerfile。接下来进行镜像的构建,从这个命令中可以看出,我们要将这个eureka-service构建的镜像标记为eureka:v1的镜像,他的全名为 192.168.244.131/microservice/eureka:v1,参数-t 是tag的意思。
.代表构建上下文(build context) 的路径,它指定了 Docker 在构建镜像时应该从哪里加载构建所需的文件。.(点):表示当前目录,即 Docker 在当前目录下寻找 Dockerfile 和其他相关文件。Docker 会将 . 目录下的所有文件(除 .dockerignore 文件中排除的文件外)打包并发送给 Docker 构建器,作为构建上下文。
[root@master eureka-service]# docker build -t 192.168.244.131/microservice/eureka:v1 .
[+] Building 347.2s (8/8) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 288B 0.0s=> [internal] load metadata for docker.io/library/openjdk:8-jdk-alpine 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/3] FROM docker.io/library/openjdk:8-jdk-alpine 0.0s=> [internal] load build context 4.8s=> => transferring context: 47.24MB 4.8s=> [2/3] RUN apk add -U tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 345.9s=> [3/3] COPY ./target/eureka-service.jar ./ 0.8s => exporting to image 0.3s => => exporting layers 0.3s => => writing image sha256:fb51d84d96efb708ef534dc356f1210293e01af0f0b1de47e0cfcc1b13cfca50 0.0s => => naming to 192.168.244.131/microservice/eureka:v1 0.0s
您在 /var/spool/mail/root 中有新邮件
[root@master eureka-service]# docker images | grep eureka
192.168.244.131/microservice/eureka v1 fb51d84d96ef 4 minutes ago 155MB
构建成功后查看镜像,可以看到我们的镜像已经存在于本地docker仓库。
随后登录harbor,然后进行镜像推送,可以看到eureka镜像已经存在于harbor中。
root@master eureka-service]# docker login 192.168.244.131
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded
您在 /var/spool/mail/root 中有新邮件
[root@master eureka-service]# docker push 192.168.244.131/microservice/eureka:v1
The push refers to repository [192.168.244.131/microservice/eureka]
dbce7a157739: Pushed
939637c19880: Pushed
ceaf9e1ebef5: Pushed
9b9b7f3d56a0: Pushed
f1b5933fe4b5: Pushed
v1: digest: sha256:ae13dc24e2f0a3ed98d3aa5178209b28e8531c32557e458364e294ad19ad9284 size: 1370
镜像部署
[root@master microservic-test]# ls
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
[root@master microservic-test]# cd k8s/
[root@master k8s]# ls
eureka-service-node.yaml gateway-service-node.yaml order.yaml portal-service-node.yaml portal.yaml.bak stock.yaml
eureka.yaml gateway.yaml order.yaml.bak portal.yaml product.yaml
我们在这个目录中回退一层,然后进入k8s目录,查看eureka的资源清单。由于我使用的是1.30版本的k8s集群,而且容器运行时采用的是containerd,所以很多东西都要重写。
未能成功的使用container的镜像拉取工具来拉取镜像,因此以下采用docker 拉取,重新打包然后导入container的方法。
修改yaml
eureka.yaml资源清单中写了ingress、service和statefulset 三个文件,这里的ingress需要修改,因为在新版的k8s中,ingress的API组已经发生了变化。新的ingress配置如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: eurekanamespace: ms
spec:ingressClassName: "nginx"rules:- host: eureka.ctnrs.comhttp:paths:- path: /pathType: Prefixbackend:service:name: eurekaport:number: 8888
同时,由于我没有成功的配置containerd从harbor拉取镜像,所以修改statefulset的配置为:imagePullPolicy:Never,随后用docker pull从harbor拉取镜像,docker -save打包,随后用ctr导入镜像。
在进行上述的配置后,kubectl apply -f eureka.yaml,随后可以看到三个实例部署成功。(因为资源清单里写的实例数量是3个,事实上我只有两个工作节点,所以有两个实例在同一个node上)
[root@master k8s]# kubectl get pods -n ms -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
eureka-0 1/1 Running 0 37m 10.244.104.62 node2 <none> <none>
eureka-1 1/1 Running 0 31m 10.244.166.136 node1 <none> <none>
eureka-2 1/1 Running 0 29m 10.244.104.63 node2 <none> <none>
因为我的ingress controller部署在node1上,所以按理来说所有的ingress发布的端口都在node1的ip上;
[root@master k8s]# kubectl get ingress -n ms
NAME CLASS HOSTS ADDRESS PORTS AGE
eureka nginx eureka.ctnrs.com 192.168.244.129 80 19m
更改本电脑hosts文件,将eureka的网址写在我们的hosts文件中。
访问eureka网址:eureka.ctnrs.com,可以看到如下页面,这里可以看到列举了所有的eureka的server全名以及端口等信息。eureka是一个服务发现的服务,目前还没有部署其他服务,所以看不到效果。
部署gateway
构建gateway镜像
同理,进入gateway-service的目录,修改Dockerfile中的镜像名称。
可以进入target目录查看gateway-service.jar。
[root@master microservic-test]# ls
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
[root@master microservic-test]# cd gateway-service/
[root@master gateway-service]# ls
Dockerfile pom.xml src target
[root@master gateway-service]# cd target/
[root@master target]# ls
classes gateway-service.jar gateway-service.jar.original generated-sources maven-archiver maven-status
[root@master target]# cd ..
[root@master gateway-service]# vim Dockerfile
[root@master gateway-service]# cat Dockerfile
FROM openjdk:8-jdk-alpine
RUN apk add -U tzdata && \ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./target/gateway-service.jar ./
EXPOSE 9999
CMD java -jar /gateway-service.jar
随后进行Dockerfile构建,成功构建镜像后上传到harbor。
[root@master gateway-service]# docker build -t 192.168.244.131/microservice/gateway:v1 .
[+] Building 3.5s (8/8) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 238B 0.0s=> [internal] load metadata for docker.io/library/openjdk:8-jdk-alpine 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/3] FROM docker.io/library/openjdk:8-jdk-alpine 0.0s=> [internal] load build context 2.0s=> => transferring context: 43.42MB 2.0s=> CACHED [2/3] RUN apk add -U tzdata && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 0.0s=> [3/3] COPY ./target/gateway-service.jar ./ 1.2s=> exporting to image 0.1s=> => exporting layers 0.1s=> => writing image sha256:1b8d154f901e41a0427cc9b51f80145139df23811700606c964aebcce36a742c 0.0s=> => naming to 192.168.244.131/microservice/gateway:v1 0.0s[root@master gateway-service]# docker images | grep gateway
192.168.244.131/microservice/gateway v1 1b8d154f901e 14 seconds ago 151MB
[root@master gateway-service]# docker login harbor
Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded
[root@master gateway-service]# docker push 192.168.244.131/microservice/gateway:v1
The push refers to repository [192.168.244.131/microservice/gateway]
972f1566a1a7: Pushed
939637c19880: Mounted from microservice/eureka
ceaf9e1ebef5: Mounted from microservice/eureka
9b9b7f3d56a0: Mounted from microservice/eureka
f1b5933fe4b5: Mounted from microservice/eureka
v1: digest: sha256:16d8b0ee21e4c8cdf1932bf575063e3b4e42284a9c93024e0819c4c4f2e3d918 size: 1370
对于两个node节点,可以进行docker pull 192.168.244.131/microservice/gateway:v1,随后进行docker save 和ctr import等命令,从而将镜像导入containerd的镜像空间。
[root@node1 images]# docker save -o gateway.v1.tar 192.168.244.131/microservice/gateway:v1
[root@node1 images]# ctr -n k8s.io images import gateway.v1.tar
unpacking 192.168.244.131/microservice/gateway:v1 (sha256:a3ea34dfccad5ae45f2806acdbe603697fb9d42b4d92b8c8a5699b2d22dc98f4)...done
当两个工作节点都有镜像后,进行pod的部署。
镜像部署
同理也是修改ingress文件和deployment文件,修改镜像名称以及拉取机制,修改hosts文件。
[root@master k8s]# kubectl get pods -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 70m
eureka-1 1/1 Running 0 63m
eureka-2 1/1 Running 0 62m
gateway-6bc9dc7657-7vnt6 1/1 Running 0 3m59s
gateway-6bc9dc7657-nvpgq 1/1 Running 0 3m59s
[root@master k8s]# kubectl get ingress -n ms
NAME CLASS HOSTS ADDRESS PORTS AGE
eureka nginx eureka.ctnrs.com 192.168.244.129 80 70m
gateway nginx gateway.ctnrs.com 192.168.244.129 80 38s
gateway也是可以通过域名访问,但是本身这只是一个网关,所以没有界面。
但是我们可以看到eureka检测到了这个服务。
进入microservic-test/gateway-service/src/main/resources目录下,查看application-fat.yaml文件,可以看到如下内容。
[root@master eureka-service]# cd /root/yaml_file/36microservice/microservic-test/gateway-service/src/main/resources
[root@master resources]# ls
application-dev.yml application-fat.yml application.yml
[root@master resources]# cat application-fat.yml
spring:
cloud:
gateway:
discovery:
locator:
#开启以服务id去注册中心上获取转发地址
enabled: true
##小写serviceId
lower-case-service-id: true
routes:
- id: product-service
uri: lb://product-service
filters:
- StripPrefix=1
predicates:
- Path=/product/**- id: order-service
uri: lb://order-service
filters:
- StripPrefix=1
predicates:
- Path=/order/**- id: stock-service
uri: lb://stock-service
filters:
- StripPrefix=1
predicates:
- Path=/stock/**eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
eureka:instance:prefer-ip-address: trueclient:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eureka
这一部分与eureka的配置有关,可以看到这里使用的eureka的实例是写死的。
在实际生产环境中一般会采用更加灵活的写法,只写上eureka的服务名称。
service-url:defaultZone: http://eureka.ms:8888/eureka
部署前端portal服务
构建portal镜像
同上,修改Dockerfile镜像名称,然后构建镜像。
上传镜像到harbor。
镜像部署
把镜像都下载导入到containerd的镜像空间,然后修改portal.yaml文件,并部署资源清单文件。
[root@master k8s]# kubectl get pods -n ms
NAME READY STATUS RESTARTS AGE
eureka-0 1/1 Running 0 98m
eureka-1 1/1 Running 0 92m
eureka-2 1/1 Running 0 91m
gateway-6bc9dc7657-7vnt6 1/1 Running 0 32m
gateway-6bc9dc7657-nvpgq 1/1 Running 0 32m
portal-54c8496c79-j6vxw 1/1 Running 0 88s
[root@master k8s]# vim portal.yaml
[root@master k8s]# kubectl get ingress -n ms
NAME CLASS HOSTS ADDRESS PORTS AGE
eureka nginx eureka.ctnrs.com 192.168.244.129 80 99m
gateway nginx gateway.ctnrs.com 192.168.244.129 80 29m
portal nginx portal.ctnrs.com 192.168.244.129 80 64s
修改hosts文件,可以通过网页浏览这个服务。
同时发现该服务也被eureka发现。
查看portal的application文件,可以看到配置了eureka。
[root@master resources]# cd /root/yaml_file/36microservice/microservic-test/portal-service/src/main/resources
[root@master resources]# cat application-fat.yml
eureka:instance:prefer-ip-address: trueclient:service-url:defaultZone: http://eureka-0.eureka.ms:8888/eureka,http://eureka-1.eureka.ms:8888/eureka,http://eureka-2.eureka.ms:8888/eurekaregister-with-eureka: truefetch-registry: truespring:freemarker:allow-request-override: falseallow-session-override: falsecache: truecharset: UTF-8check-template-location: truecontent-type: text/htmlenabled: trueexpose-request-attributes: falseexpose-session-attributes: falseexpose-spring-macro-helpers: trueprefer-file-system-access: truesuffix: .ftltemplate-loader-path: classpath:/templates/
部署product、order和stock服务
这三个服务没有ingress和service,只需要修改Dockerfile以及deployment文件,构建镜像和pod之后,就可以被eureka发现。
值得注意的是,这三个文件夹里面会有两个子文件夹,进入后缀为biz的文件夹,就可以看到dockerfile等文件。
[root@master microservic-test]# ls
basic-common eureka-service k8s lombok.config pom.xml product-service stock-service
db gateway-service LICENSE order-service portal-service README.md
[root@master microservic-test]# cd product-service/
[root@master product-service]# ls
pom.xml product-service-api product-service-biz
[root@master product-service]# cd product-service-biz/
[root@master product-service-biz]# ls
Dockerfile pom.xml src target
此时我已经部署完所有的服务,可以正常使用这个电商网站了。