【问题标题】:Can't connect spring boot to postgres k8s statefulset无法将spring boot连接到postgres k8s statefulset
【发布时间】:2021-12-11 03:39:46
【问题描述】:

我是 Kubernetes 新手,我是第一次在 Kubernetes 上部署应用程序。我想部署一个 postgreSQL statefulset 和一个简单的 spring boot pod 副本集。我创建了一个无头服务,它将附加到以下 statefulset。

    # Headless Service
apiVersion: v1
kind: Service
metadata:
  name: ecom-db-h
spec:
  ports:
  - targetPort: 5432
    port: 80
  clusterIP: None
  selector:
    app: ecom-db
---
# PostgreSQL StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: ecomdb-statefulset
  labels:
    app: ecom-db
spec:
  serviceName: ecom-db-h
  selector:
    matchLabels:
      app: postgresql-db
  replicas: 2
  template:
    # Pod Definition
    metadata:
      labels:
        app: postgresql-db
    spec:
      containers:
        - name: db
          image: postgres:13
          ports:
            - name: postgres
              containerPort: 5432
         # volumeMounts:
          #  - name: ecom-db
           #   mountPath: /var/lib/postgresql/data
          env:
            - name: POSTGRES_DB
              value: ecommerce
            - name: POSTGRES_USER
              value: postgres
            - name: POSTGRES_PASSWORD
              value: <password>
      #volumes:
       # - name: ecom-db
        #  persistentVolumeClaim:
         #   claimName: ecom-pvc

这是我为 Spring Boot 应用程序 pod 创建的副本集:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: ecom-server
  labels:
    app: ecom-server
    tier: backend
spec:
  # modify replicas according to your case
  replicas: 2
  selector:
    matchLabels:
      type: backend
  template:
    # Pod definition
    metadata:
      labels:
        app: ecom-server
        type: backend
    spec:
      containers:
      - name: ecommerce-server
        image: <my.private.repo.url>/spring-server:kubernetes-test
        imagePullPolicy: Always
      imagePullSecrets:
          - name: regcred

我使用的是默认命名空间,这里是 application.properties:

spring.datasource.url=jdbc:postgresql://ecom-db-h.default.svc.cluster.local/ecommerce?useSSL=false
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.format_sql=true
spring.jpa.hibernate.id.new_generator_mappings=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect

但是我的 spring boot 容器总是退出并出现以下错误:

Caused by: java.net.UnknownHostException: ecom-db-h.default.svc.cluster.local
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at org.postgresql.core.PGStream.createSocket(PGStream.java:231)
        at org.postgresql.core.PGStream.<init>(PGStream.java:95)
        at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:98)
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213)
        ... 136 common frames omitted
  

这意味着副本集无法找到无头服务,或者我的无头服务根本没有连接到有状态的 Pod。我错过了 YAML 文件中的某些内容吗?还是我以错误的方式使用无头服务?


更新:

感谢@Thomas 和@harsh-manvar,我了解到无头服务并不意味着连接到数据库。相反,我应该使用普通的 ClusterIP 服务。问题是我这样做了,但我仍然遇到同样的错误。这是我的新 YAML 文件:

application.properties 修改:

spring.datasource.url=jdbc:postgresql://db-svc.default.svc.cluster.local/ecommerce?useSSL=false

statefulset.yaml 修改

# ClusterIP service instead of the headless service
apiVersion: v1
kind: Service
metadata:
  name: db-svc
spec:
  ports:
  - name: pgql
    port: 80
    targetPort: 5432
    protocol: TCP
  selector:
    app: db
---
# PostgreSQL StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db-sts
  labels:
    app: db-sts
spec:
  serviceName: db-svc
  selector:
    matchLabels:
      app: db
  replicas: 2
  template:
    # Pod Definition
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: db
          image: postgres:13
          ports:
            - name: postgres
              containerPort: 5432
         # volumeMounts:
          #  - name: ecom-db
           #   mountPath: /var/lib/postgresql/data
          env:
            - name: POSTGRES_DB
              value: ecommerce
            - name: POSTGRES_USER
              value: postgres
            - name: POSTGRES_PASSWORD
              value: mss#123
      #volumes:
       # - name: ecom-db
        #  persistentVolumeClaim:
         #   claimName: ecom-pvc

这是新的错误:

Caused by: java.net.UnknownHostException: db-svc.default.svc.cluster.local
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at org.postgresql.core.PGStream.createSocket(PGStream.java:231)
        at org.postgresql.core.PGStream.<init>(PGStream.java:95)
        at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:98)
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213)
        ... 136 common frames omitted

我的 statefulset pod 运行顺利,服务已创建:

NAME                      READY   AGE
statefulset.apps/db-sts   2/2     26m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/db-svc       ClusterIP   10.108.39.189   <none>        5432/TCP   26m
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    30m

NAME                READY   STATUS             RESTARTS        AGE
db-sts-0            1/1     Running            0               26m
db-sts-1            1/1     Running            0               26m
ecom-server-5jbs2   0/1     CrashLoopBackOff   9 (44s ago)     25m
ecom-server-rtpmg   0/1     CrashLoopBackOff   8 (4m46s ago)   25m

NAME         ENDPOINTS                           AGE
db-svc       10.244.1.48:5432,10.244.2.85:5432   18h

第二次更新:

在 pod 中运行 alpine-shell 容器以检查 DNS 解析后,我执行了“nslookup db-svc.default.svc.cluster.local”并得到了这个:

nslookup db-svc.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   db-svc.default.svc.cluster.local
Address: 10.107.101.52

这意味着服务确实存在并且可以从这个容器中访问它。奇怪的是,当我执行 pgsql 命令尝试连接到 postgres 数据库服务器时,我根本没有得到任何响应,命令甚至没有退出。这是错误

WARN  SqlExceptionHelper - SQL Error: 0, SQLState: 08001
ERROR SqlExceptionHelper - The connection attempt failed.
Caused by: org.postgresql.util.PSQLException: The connection attempt failed.
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:315)
        at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51)
        at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:225)
        at org.postgresql.Driver.makeConnection(Driver.java:465)
        at org.postgresql.Driver.connect(Driver.java:264)
        at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
        at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358)
        at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
        at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:477)
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:560)
        at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115)
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
        at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
        at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180)
        at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:43)
        ... 122 common frames omitted
Caused by: java.net.SocketTimeoutException: connect timed out
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:607)
        at org.postgresql.core.PGStream.createSocket(PGStream.java:231)
        at org.postgresql.core.PGStream.<init>(PGStream.java:95)
        at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:98)
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213)
        ... 136 common frames omitted

【问题讨论】:

    标签: java spring postgresql spring-boot kubernetes


    【解决方案1】:

    headless 服务的定义是不提供 DNS 记录并提供内部负载平衡。

    无头服务可用于查询端点并单独处理它们。

    要解决您的问题,请创建常规服务。

    【讨论】:

    • 问题解决了,我贴出答案,你帮了我。太感谢了。但是如果我不指定无头服务,我仍然对 spring boot 如何将“write”请求转发到 postgres master pod 感到困惑。如果我改为键入 ClusterIP,它可能会修改/插入其中一个从属服务器(从属 n)中的记录;因此主服务器和从服务器 x
    【解决方案2】:

    无头服务不会给你 DNS 记录,你应该是 ClusterIP 或 NodePort 或 LoadBalancer 中的任何一个

    无头服务是具有服务 IP 但不是 负载平衡它将返回我们关联 Pod 的 IP。这 允许我们直接与 Pod 交互而不是代理。它是 就像为 .spec.clusterIP 指定 None 一样简单,并且可以使用 带或不带选择器 - 您将看到一个带有选择器的示例 时刻。

    阅读更多关于无头服务的信息:https://dev.to/kaoskater08/building-a-headless-service-in-kubernetes-3bk8

    您可以按照以下创建服务类型的 YAML 文件:ClusterIP

    apiVersion: v1
    kind: Service
    metadata:
      name: postgres
    spec:
      ports:
      - name: pgql
        port: 5432
        targetPort: 5432
        protocol: TCP
      selector:
        app: postgres
    ---
    apiVersion: apps/v1  
    kind: StatefulSet
    metadata:
      name: postgres
    spec:
      serviceName: "postgres"
      replicas: 1
      selector:
        matchLabels:
          app: postgres
      template:
        metadata:
          labels:
            app: postgres
        spec:
          containers:
          - name: postgres
            image: postgres:9.5
            volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
              subPath: pgdata
            env:
            - name: POSTGRES_USER
              value: root
            - name: POSTGRES_PASSWORD
              value: password
            - name: POSTGRES_DB
              value: kong
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
            ports:
            - containerPort: 5432
          terminationGracePeriodSeconds: 60
      volumeClaimTemplates:
      - metadata:
          name: postgres-data
        spec:
          accessModes:
          - "ReadWriteOnce"
          resources:
            requests:
              storage: 10Gi
    

    https://github.com/harsh4870/Keycloack-postgres-kubernetes-deployment/blob/main/postgres.yaml

    【讨论】:

    • 谢谢。附加到 statefulset 的 serviceName 不应该是无头服务吗?
    • 如果您的应用程序想要与 Postgres 连接,您应该使用服务类型 ClusterIP。无头服务将为您提供 POD 的 IP,并且您的应用程序必须连接到该 IP 上的 POD,如果 pod 重新启动或崩溃,这可能会随着时间而改变,因此最佳实践是使用具有 clusterIP 类型的服务名称。
    • 您可以结帐:medium.com/swlh/…
    • 这里也是代码中的一个很好的例子:medium.com/swlh/… 你需要使用 IP 而不是服务名称,这是不好的做法。
    • 是的,这可能是问题我建议通过启动busybox容器并尝试使用cli连接来检查这个问题它是否工作。我们开始吧:gist.github.com/so0k/860b570276a86adc38b75cb859bf4c16
    【解决方案3】:

    终于找到了解决办法:

    原帖: 首先我需要使用 ClusterIP 服务而不是无头服务。

    更新解决方案: 是防火墙阻止了我服务中的 pod 之间的请求。这就是为什么我收到错误 SQLState: 08001。禁用防火墙解决了这个问题。我还需要在 application.properties 中指定端口。因为我习惯了不使用 HTTP 连接指定端口 80,所以我认为 postgres 连接到数据库就是这种情况,但我需要指定端口:

    spring.datasource.url=jdbc:postgresql://db-svc.default.svc.cluster.local:80/ecommerce?useSSL=false
    

    最好将服务端口更改为默认的 postgres 服务器端口:5432

    【讨论】:

      猜你喜欢
      • 2020-03-14
      • 2020-04-23
      • 2021-09-16
      • 2016-02-11
      • 1970-01-01
      • 1970-01-01
      • 2022-11-01
      • 2018-09-07
      • 2020-08-18
      相关资源
      最近更新 更多