Skip to main content

k8s实验三 用服务实现应用互访

本实验将创建2个 Nginx(版本 1.19.0) 应用,用域名的方式实现2个 Pod 互相访问的功能。

如果是用 docker 实现

常用 docker-compose 配置多个应用,同一网络内的应用可以根据服务名称互相访问。

docker-compose.yaml

version: '3'

services:
  nginx1:
    container_name: example3nginx1
    image: nginx:1.19.0
  nginx2:
    container_name: example3nginx2
    image: nginx:1.19.0

上面的 docker-compose 例子没有指定网络,docker-compose 会自动生成网络。

检查运行结果

# 去 nginx1 访问 nginx2
docker exec example3nginx1 curl nginx2
# 去 nginx2 访问 nginx1
docker exec example3nginx2 curl nginx1

现在在 k8s 上试运行,现在每个步骤都有配置文件,直接 kubectl apply -f <配置文件> 即可,故标题不再重复,具体请参考前几个实验。

创建 ConfigMap,存储键值对(key-value)数据

在 docker 部署应用经常需要环境变量(ENV),但是 ENV 有时候不是写死的,所以 docker-compose 创建服务时会读取 .env 文件,这个 .env 就是一个简单的键值对(key-value)文件。

k8s 提供一个更多功能的键值对,称 ConfigMap。像卷一样,ConfigMap 也要提前创建。

configmap.yaml
# Source configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: example3configmap
data:

  example3_nginx1_myenv: nginx1_MY_ENV_VALUE
  example3_nginx2_myenv: nginx2_MY_ENV_VALUE
 
  nginx1_default.conf: |
    server {
      listen *:80;
      server_name default_server;
      location / {
        return 200 'Nginx1';
      }
    }    

  nginx2_default.conf: |
    server {
      listen *:80;
      server_name default_server;
      location / {
        return 200 'Nginx2';
      }
    }    
configmap.yaml 带注释
# Source configmap.yaml
# 解析配置文件的版本,v1即可
apiVersion: v1
# 资源的类型,ConfigMap
kind: ConfigMap
# 元数据
metadata:
  # 名称
  name: example3configmap

# 键值对数据
data:
  # 键值对 example3_nginx1_myenv: nginx1_MY_ENV_VALUE
  example3_nginx1_myenv: nginx1_MY_ENV_VALUE
  # 键值对 example3_nginx2_myenv: nginx2_MY_ENV_VALUE
  example3_nginx2_myenv: nginx2_MY_ENV_VALUE
  # 键值对 nginx1_default.conf,值可以是多行的,可以理解成文件内容,作为应用 nginx1 的配置文件
  nginx1_default.conf: |
    server {
      listen *:80;
      server_name default_server;
      location / {
        return 200 'Nginx1';
      }
    }    
  # 同上,作为应用 nginx2 的配置文件
  nginx2_default.conf: |
    server {
      listen *:80;
      server_name default_server;
      location / {
        return 200 'Nginx2';
      }
    }    

详细字段说明

  • apiVersion 解析配置文件的版本,v1即可。
  • kind 资源的类型,ConfigMap。
  • metadata 资源的元数据。
  • metadata.name ConfigMap 的名称。
  • data ConfigMap 定义键值对数据。
  • data.example3_nginx1_myenv 定义键值对 example3_nginx1_myenv,值为 nginx1_MY_ENV_VALUE
  • data.example3_nginx2_myenv 同上,定义键值对 example3_nginx2_myenv,值为 nginx2_MY_ENV_VALUE
  • data.nginx1_default.conf 定义键值对 nginx1_default.conf,值可以是多行的,可以理解成文件内容。
  • data.nginx2_default.conf 定义键值对 nginx2_default.conf,同上,这两个文件将作为 nginx 的配置文件。

检查运行结果

kubectl get configmap
kubectl describe configmap example3configmap

ConfigMap 的一个特点是可以将值映射成文件内容,而 docker 要在本机编写好文件,再用卷映射到容器(当然 docker 也可以用 echo 的方法输出文件,但是遇到多行内容会麻烦)。接下来的步骤会展示 ConfigMap 的使用方法。

创建 Nginx 1号应用,访问返回 Nginx1,配置文件取自 ConfigMap

pod1.yaml
# Source: pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: example3nginx1
  labels:
    app: example3nginx1
spec:
  containers:
    - name: example3nginx1
      image: nginx:1.19.0
      imagePullPolicy: IfNotPresent
      env:
        - name: MY_ENV_FROM_VALUE
          value: i_am_from_value
        - name: MY_ENV_FROM_CONFIG
          valueFrom:
            configMapKeyRef:
              name: example3configmap
              key: example3_nginx1_myenv
      volumeMounts:
        - mountPath: /etc/nginx/conf.d
          name: example3nginx1volume
          readOnly: true
  volumes:
    - name: example3nginx1volume
      configMap:
        name: example3configmap
        items:
          - key: 'nginx1_default.conf'
            path: 'default.conf'
pod1.yaml 带注释
# Source: pod1.yaml
# v1
apiVersion: v1
# 资源类型,Pod
kind: Pod
# 元数据
metadata:
  # Pod 名称
  name: example3nginx1
  # Pod 标签
  labels:
    # 自定义 Pod 标签 app: example3nginx1
    app: example3nginx1
# Pod 描述
spec:
  # 运行哪些容器
  containers:
      # 容器名
    - name: example3nginx1
      # 容器镜像
      image: nginx:1.19.0
      # 镜像拉取策略
      imagePullPolicy: IfNotPresent
      # 容器环境变量
      env:
          # 定义环境变量 MY_ENV_FROM_VALUE="i_am_from_value"
        - name: MY_ENV_FROM_VALUE
          value: i_am_from_value
          # 定义环境变量 MY_ENV_FROM_CONFIG,变量值取自 ConfigMap 的数据 example3_nginx1_myenv
        - name: MY_ENV_FROM_CONFIG
          valueFrom:
            configMapKeyRef:
              name: example3configmap
              key: example3_nginx1_myenv
      # 挂载临时卷
      volumeMounts:
          # 挂载一个临时卷 example3nginx1volume,挂载至容器路径 /etc/nginx/conf.d
        - name: example3nginx1volume
          mountPath: /etc/nginx/conf.d
          readOnly: true # 只读
  # 临时卷
  volumes:
      # 创建一个临时卷 example3nginx1volume
    - name: example3nginx1volume
      # 将 ConfigMap 的数据映射为文件
      configMap:
        name: example3configmap
        items:
            # 映射键 nginx1_default.conf,保存至文件 default.conf,存放到临时卷
          - key: 'nginx1_default.conf'
            path: 'default.conf'

详细字段说明

  • apiVersion v1。
  • kind Pod。
  • metadata 元数据。
  • metadata.name Pod 名称。
  • labels Pod 标签。
  • labels.app 定义 Pod 标签 app,值为 example3nginx1,创建服务时,会根据这个标签找到这个Pod。
  • spec Pod 参数。
  • spec.containers 定义 Pod 运行哪些容器。
  • spec.containers.name 容器名。
  • spec.containers.image 容器镜像。
  • spec.containers.imagePullPolicy 镜像拉取策略,IfNotPresent 表示没有镜像时再去拉取。
  • spec.containers.env 定义容器环境变量(ENV)。
  • spec.containers.env.name 环境变量名。
  • spec.containers.env.value 环境变量的值。
  • spec.containers.env.valueFrom.configMapKeyRef 声明该环境变量的值来自 ConfigMap
  • spec.containers.env.valueFrom.configMapKeyRef.name ConfigMap 的名称。
  • spec.containers.env.valueFrom.configMapKeyRef.key ConfigMap 的键,k8s 会根据键得到值。
  • spec.containers.volumeMounts 配置临时卷的绑定关系。
  • spec.containers.volumeMounts.mountPath 临时卷挂载点。
  • spec.containers.volumeMounts.name 临时卷名称。
  • spec.containers.volumeMounts.readOnly 声明临时卷只读。
  • spec.volumes 声明临时卷。
  • spec.volumes.name 临时卷名称。
  • spec.volumes.configMap 声明临时卷的部分文件来自 ConfigMap
  • spec.volumes.configMap.name ConfigMap 的名称。
  • spec.volumes.configMap.items 配置从 ConfigMap 中映射出来的文件。
  • spec.volumes.configMap.items.key ConfigMap 键。
  • spec.volumes.configMap.items.path 该往哪个文件放入值,default.conf 是 Nginx 默认配置文件,加上挂载点后的目录就是 <挂载点>/<文件名>

现在定义了一个 Pod,配置文件是从 ConfigMap 获取的,根据键 nginx1_default.conf 去查 ConfigMap,可以得到 Nginx 配置文件:

# 这个内容来自 ConfigMap -> nginx1_default.conf
# 将被挂载到 /etc/nginx/conf.d,文件名 default.conf
server {
  listen *:80;
  server_name default_server;
  location / {
    return 200 'Nginx1';
  }
}

这份 Nginx 配置文件简单描述了 「访问根目录时,返回字符串 “Nginx1”」。现在可以先部署这个 Pod。

为这个 Pod 创建服务 (Service)

pod1svc.yaml
kind: Service
metadata:
  name: example3nginx1svc
spec:
  type: ClusterIP
  selector:
    app: example3nginx1
  ports:
    - port: 80
      targetPort: 80
pod1svc.yaml 带注释
# Source: pod1svc.yaml
kind: Service
metadata:
  name: example3nginx1svc
spec:
  # 端口映射类型,ClusterIP(集群内网访问)
  type: ClusterIP
  # 标签过滤
  selector:
    # 带有标签 app: example3nginx1 的 Pod 会被映射端口
    app: example3nginx1
  # 端口配置
  ports:
      # 内网访问端口
    - port: 80
      # Pod 中的端口
      targetPort: 80

Service 字段简单说明

这份服务配置文件相对简单了,它的工作是找到标签 app = example3nginx1 的 Pod,然后给它提供内网端口映射。具体的字段在之前的实验已经描述过了,简单说一下吧:

  • spec Service 的参数。
  • spec.ports Service 需要映射的端口。
  • spec.ports.port 内网映射出来的端口。
  • spec.ports.targetPort Pod 的端口,注意不要和 port 搞混。

现在把这个服务部署起来。

服务可以为 Pod 提供 DNS 记录

当这个服务和 Nginx1 Pod 绑定成功后,其他 Pod 都可以通过域名访问到该 Pod 了(亲测其他 namespace 也可以)。

域名格式: <Service名>.<Namespace名>.svc.<节点域名>

  • <Service名> 服务的名称,上方的服务名为 example3nginx1svc
  • <Namespace名> 命名空间,可用 kubectl get namespaces 查看,没有提及命名空间的都是 default
  • <集群域名> k8s 集群域名,可以进 Pod 查看(kubectl exec example3nginx1 -- cat /etc/resolv.conf),一般是 cluster.local

按照格式组装好的域名是: example3nginx1svc.default.svc.cluster.local,带这个域名进入 Pod 解析并发起请求,可以得到结果 Nginx1:

kubectl exec example3nginx1 -- curl example3nginx1svc.default.svc.cluster.local

不过这个是在自己的 Pod 下面执行的,对于是否可以在其他应用下调用到 Nginx1 应用,请继续往下看。

创建 Nginx 2号应用,访问返回 Nginx2,配置文件也取自 ConfigMap,并配置服务

和上面 Pod 和 Service 的配置一样,只是名字和标签修改了而已。具体的字段说明就不一一列出了,但请注意加 # 号,会展示与 Nginx1号 配置不一样的地方

另外 k8s yaml 支持在一个文件中配置多个资源对象,之间用新一行 --- 隔开。

pod2full.yaml 请注意 # 号与 nginx1 配置不一样的地方
# 源文件: pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: example3nginx2 #
  labels:
    app: example3nginx2 #
spec:
  containers:
    - name: example3nginx2 #
      image: nginx:1.19.0
      imagePullPolicy: IfNotPresent
      env:
        - name: MY_ENV_FROM_VALUE
          value: i_am_from_value
        - name: MY_ENV_FROM_CONFIG
          valueFrom:
            configMapKeyRef:
              name: example3configmap
              key: example3_nginx2_myenv #
      volumeMounts:
        - mountPath: /etc/nginx/conf.d
          name: example3nginx2volume #
          readOnly: true
  volumes:
    - name: example3nginx2volume #
      configMap:
        name: example3configmap
        items:
          - key: 'nginx2_default.conf' #
            path: 'default.conf'
---
# 源文件: pod2svc.yaml
# 给 Nginx2号 应用配置服务
kind: Service
metadata:
  name: example3nginx2svc #
spec:
  type: ClusterIP
  selector:
    app: example3nginx2 #
  ports:
    - port: 80
      targetPort: 80

直接启动,k8s 将创建2个资源对象:

  • Pod对象,名称 example3nginx2。
  • Service对象,名称 example3nginx2svc。

验证互访

Nginx1、Nginx2 的 Pod 已经部署好了,并且各自都配置了服务,那么总结出2个服务的 DNS 记录。

服务概览

应用 Pod名称 服务名称 域名
Nginx1 example3nginx1 example3nginx1svc example3nginx1svc.default.svc.local
Nginx2 example3nginx2 example3nginx2svc example3nginx2svc.default.svc.local

互相进入 Pod,访问对方

进入 Nginx1,访问 Nginx2

kubectl exec example3nginx1 -- curl example3nginx2svc.default.svc.local
# 获得响应 Nginx2

进入 Nginx2,访问 Nginx1

kubectl exec example3nginx2 -- curl example3nginx1svc.default.svc.local
# 获得响应 Nginx1

预期结果无误,那么实验顺利结束了。

清理部署资源

本次实验用的资源比较多,下面一一列举出来:

资源 kind 名称(name) 通过名称删除
键值对配置 ConfigMap example3configmap kubectl delete configmap example3configmap
Nginx1应用 Pod example3nginx1 kubectl delete pod example3nginx1
Nginx2应用 Pod example3nginx2 kubectl delete pod example3nginx2
Nginx1服务 Service example3nginx1svc kubectl delete svc example3nginx1svc
Nginx2服务 Service example3nginx2svc kubectl delete svc example3nginx2svc