Kubernetes 与Docker:如何让两个服务正确通信

Kubernetes and Docker: how to let two service to communicate correctly

我有两个 Java 微服务(caller.jar 调用 called.jar

我们可以通过 env-var CALLERPORT 设置 caller 服务 http 端口和通过 env-var CALLEDADDRESS 调用服务的地址。 所以调用者使用了两个环境变量。

我们还必须设置 called service env-var CALLEDPORT 以设置被调用服务正在侦听的特定 http 端口http 请求。

我不知道如何简单地从 Docker 文件公开这些变量,以便使用 Kubernetes 设置它们。

这是我制作两个 Docker 文件的方法:

Docker来电者档案

FROM openjdk:8-jdk-alpine

# ENV CALLERPORT     (it's own port)
# ENV CALLEDADDRESS  (the other service address)

ADD caller.jar /

CMD ["java", "-jar", "caller.jar"]

Docker调用的文件

FROM openjdk:8-jdk-alpine

# ENV CALLEDPORT (it's own port)

ADD called.jar /

CMD ["java", "-jar", "called.jar"]

用这些我制作了两张 Docker 图片:

然后我制作了两个 deployments.yaml 以便让 K8s 使用副本和负载均衡器部署(在 minikube 上)这两个微服务。

部署-caller.yaml

apiVersion: v1
kind: Service              
metadata:
  name: caller-loadbalancer
spec:
  type: LoadBalancer       
  ports:
  - port: 8080               
    targetPort: 8080        
  selector:            
    app: caller    
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: caller
  labels:
    app: caller
spec:
  replicas: 2                                             
  minReadySeconds: 15
  strategy:
    type: RollingUpdate                                   
    rollingUpdate: 
      maxUnavailable: 1                                   
      maxSurge: 1                                         
  selector:
    matchLabels:
      app: caller
      tier: caller
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: caller
        tier: caller
    spec:
      containers:
      - image: myaccount/caller
        name: caller
        env:
        - name: CALLERPORT
          value: "8080"
        - name: CALLEDADDRESS
          value: called-loadbalancer  # WHAT TO PUT HERE?!
        ports:
        - containerPort: 8080
          name: caller

部署-called.yaml

apiVersion: v1
kind: Service              
metadata:
  name: called-loadbalancer
spec:
  type: LoadBalancer       
  ports:
  - port: 8081               
    targetPort: 8081        
  selector:            
    app: called    
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: called
  labels:
    app: called
spec:
  replicas: 2                                             
  minReadySeconds: 15
  strategy:
    type: RollingUpdate                                   
    rollingUpdate: 
      maxUnavailable: 1                                   
      maxSurge: 1                                         
  selector:
    matchLabels:
      app: called
      tier: called
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: called
        tier: called
    spec:
      containers:
      - image: myaccount/called
        name: called
        env:
        - name: CALLEDPORT
          value: "8081"
        ports:
        - containerPort: 8081
          name: called

重要提示: 如果单独调用单个服务(例如调用健康检查端点),则单个服务运行良好,但是当调用涉及两个服务之间通信的端点时,则会出现此错误:

java.net.UnknownHostException: called

pods 正确 运行 并且处于活动状态,但我想问题出在 deployment.yaml 的部分,我必须在其中定义如何找到指向的服务,所以在这里:

spec:
  containers:
    - image: myaccount/caller
      name: caller
      env:
        - name: CALLERPORT
          value: "8080"
        - name: CALLEDADDRESS
          value: called-loadbalancer  # WHAT TO PUT HERE?!
        ports:
        - containerPort: 8080
          name: caller

都没有

called

也不

called-loadbalancer

也不

http://caller 


kubectl get pods,svc -o wide

NAME                          READY     STATUS    RESTARTS   AGE       IP           NODE       NOMINATED NODE   READINESS GATES
pod/called-855cc4d89b-4gf97   1/1       Running   0          3m23s     172.17.0.4   minikube   <none>           <none>
pod/called-855cc4d89b-6268l   1/1       Running   0          3m23s     172.17.0.5   minikube   <none>           <none>
pod/caller-696956867b-9n7zc   1/1       Running   0          106s      172.17.0.6   minikube   <none>           <none>
pod/caller-696956867b-djwsn   1/1       Running   0          106s      172.17.0.7   minikube   <none>           <none>

NAME                          TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE       SELECTOR
service/called-loadbalancer   LoadBalancer   10.99.14.91    <pending>     8081:30161/TCP   171m      app=called
service/caller-loadbalancer   LoadBalancer   10.107.9.108   <pending>     8080:30078/TCP   65m       app=caller
service/kubernetes            ClusterIP      10.96.0.1      <none>        443/TCP          177m      <none>

如果放在 deployment.yaml 的那一行就可以工作。 那么这一行应该放什么呢?

简短的回答是您不需要在 Dockerfile 中公开它们。您可以在启动容器时设置所需的任何环境变量,而不必预先在 Dockerfile 中指定它们。

您可以通过使用 'docker run' 启动容器来验证这一点,使用“-e”设置环境变量,使用“-it”获取交互式会话。回显您的环境变量的值,您会看到它已设置。

您还可以使用 运行 kubernetes Pod 中的一个容器与 'kubectl exec' (https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/) 进行终端会话。从那里您可以从那里回显环境变量以查看它们是否已设置。使用 'kubectl get pods' 获取 pod 名称后,您可以使用 'kubectl describe pod ' 更快地查看它们。

既然您遇到了问题,您还想检查您的服务是否正常运行。由于您使用的是 minikube,因此您可以执行 'minikube service ' 来检查它们是否可以从外部访问。您还需要检查内部访问 - 请参阅

您使用服务名称和端口的方法是有效的。通过一些调试,你应该能够让它工作。您的设置类似于我在 https://dzone.com/articles/kubernetes-namespaces-explained 中所做的插图,因此参考它可能会有所帮助(除非您直接使用环境变量而不是通过配置映射,但它是一样的)。

我认为在调用者中,您在环境变量中注入了错误的端口 - 您放置了调用者自己的端口,而不是它试图调用的端口。

要访问 Kubernetes 内的服务,您应该使用此 DNS:

首先-完全不可能理解你想要什么。您的 post 起始于:

We can set...

We must set...

这里没有人不知道您想做什么,如果能看到您期望的完成的一些定义会更有用。

说到这里,我要转向你的实质性问题了...

  env:
    - name: CALLERPORT
      value: "8080"
    - name: CALLEDADDRESS
      value: called-loadbalancer  # WHAT TO PUT HERE?!
    ports:
    - containerPort: 8080
      name: caller

这个东西会被k8s自动导出。例如,我的服务 kibana 在服务定义中带有 port:80

svc/kibana                                 ClusterIP      10.222.81.249    <none>                              80/TCP              1y        app=kibana

这就是我在同一命名空间中的不同 pod 中获取它的方法:

root@some-pod:/app# env | grep -i kibana
KIBANA_SERVICE_PORT=80
KIBANA_SERVICE_HOST=10.222.81.249

展望未来,您为什么使用 LoadBalancer?没有任何云,它将类似于 NodePort,但似乎 ClusterIP 就是您所需要的。 接下来,服务端口可以相同,不会有任何端口冲突,只是因为 ClusterIP 每次都是唯一的,因此每个服务的套接字都是唯一的。您的服务可以这样描述:

apiVersion: v1
kind: Service              
metadata:
  name: caller-loadbalancer
spec:
  type: LoadBalancer       
  ports:
  - port: 80 <--------------------           
    targetPort: 8080        
  selector:            
    app: caller  

apiVersion: v1
kind: Service              
metadata:
  name: called-loadbalancer
spec:
  type: LoadBalancer       
  ports:
  - port: 80 <------------------             
    targetPort: 8081        
  selector:            
    app: called

这将简化仅通过名称而不指定端口的服务名称的使用:

http://caller-loadbalancer.default.svc.cluster.local
http://called-loadbalancer.default.svc.cluster.local

http://caller-loadbalancer.default
http://called-loadbalancer.default

或(在类似的命名空间内):

http://caller-loadbalancer
http://called-loadbalancer

或(取决于库)

caller-loadbalancer
called-loadbalancer

containerPort/targetPort 也一样!为什么使用 80818080?谁关心内部集装箱港口?我同意会发生不同的情况,但在这种情况下,您内部只有一个进程,您肯定不会 运行 那里有更多进程,是吗?所以他们也可能是一样的。

我想建议您以不同的方式使用 Whosebug。不要问如何以你的方式做事,最好问如何以最好的方式做事