Parksmap App

在本实验中,我们将部署 ParksMap 应用程序的 Web 组件,该应用程序也称为parksmap,并使用 OpenShift 的服务发现机制来发现部署的后端服务并在地图上显示其数据。

Application architecture

练习: 部署您的第一个镜像 Deploying your First Image

让我们从做最简单的事情开始 - 获取一个Docker 格式的图像以在 OpenShift 上运行。这非常简单。使用 OpenShift,它可以直接从 Web 控制台完成。

返回到 Web Console.

如果您不再处于开发人员视图,请立即返回。

从左侧菜单中,单击+添加。您将看到一个屏幕,您可以在其中使用多个选项将应用程序部署到 OpenShift。点击*容器镜像* Container Image 打开一个对话框,您可以在其中指定要部署的映像的信息。

Add from Container Image

在镜像名称字段中,将以下内容复制/粘贴到框中:

quay.io/openshiftroadshow/parksmap:latest

然后OpenShift将转到指定的容器注册表并查询图像。

您的屏幕最终将看起来像这样:

Explore Project

在运行时图标*Runtime Icon*中,您可以选择要在应用程序的 OpenShift 拓扑视图中使用的图标。你可以保留默认的 OpenShift 图标,或者因为这个前端是用 Spring Boot 制作的,你可以选择spring-boot。

确保在以下位置变量数值正确:

Application Name :

workshop

Name :

parksmap

确保从资源*Resource*部分选择部署*Deployment* 。

我们将在下一章详细讨论Deployment和DeploymentConfig。

取消选中Create a route to the application旁边的复选框。出于学习目的,我们将在实验的稍后部分为应用程序创建一个路由。

在页面的底部,点击标签在高级选项部分,并添加一些标签*Labels*以后更好地识别此部署。标签将帮助我们在 Web 控制台和命令行中识别和过滤组件。

我们将添加 3 个标签 为每个标签输入名称=值对后,在键入下一个标签前, 按 Return (或 Enter 取决于您的键盘) 以输入下一个标签。我们将输入的第一个标签是应用程序的名称:

app=workshop

接下来是此部署deployment的名称。

component=parksmap

最后,标记这个组件在整个应用程序中扮演的角色

role=frontend
Deploy image

接下来,单击蓝色的创建按钮。您将被定向到拓扑页面,您应该在其中看到应用程序中parksmap部署配置的可视化workshop。

Topology View with Parksmap

您在 OpenShift 上部署容器映像只需要这几个步骤。这应该适用于任何遵循最佳实践的容器镜像,例如定义一个 EXPOSE 端口,不需要专门作为root 用户或其他用户名运行,以及在启动容器时执行非退出命令。

由于openshift在安全、功能等方面在k8s基础上做了很多修改及增强,所以强烈建议参考红帽openshift官方文档, 《学习容器最佳实践章节,(您可能需要红帽订阅或免费申请红帽开发者账号)

出于组织目的部署复杂应用程序时,需要使用适当的标签标记使用的资源。OpenShift 使用标签应用程序在概览页面中定义和分组组件资源。如果用户未明确提供,OpenShift 将使用一些默认值创建此标签。

背景: 容器和pods Containers and Pods

在开始深入研究之前,我们需要了解容器和Pod之间的关系。我们不会在本实验室中介绍这些技术的背景,但如果您有任何疑问,请告知讲师。下面我们将深入研究并开始使用它们。

在 OpenShift (k8s)中,最小的可部署单元是Pod, *Pod*是一组部署在一起并保证位于同一主机上的一个或多个OCI容器。 来自官方 OpenShift 文档:

每个Pod都有自己的 IP 地址,因此拥有自己的整个端口空间port space,Pod 内的容器可以共享存储。Pod可以用一个或多个标签“标记”,然后用于在单个操作中选择和管理Pod组。

Pod可以包含多个 OCI 容器。总体思路是让Pod包含一个“主进程”以及您希望与该进程一起运行的任何辅助服务。例如您可以在Pod中放置的容器包括Apache HTTPD 服务器、日志分析器和帮助管理上传文件的文件服务。

练习:查看 Pod信息

如果单击parksmap拓扑视图中的条目,您将看到有关该部署配置的一些信息。在资源选项卡可能被默认显示。如果是这样,请单击“详细信息”选项卡。

Details Tab image

在该面板上,您将看到有一个由您的操作创建的Pod。

Pod overview
您会在此视图中注意到一个信息框,建议为我们的应用添加健康检查。我们将在稍后详细讨论它,因此目前您只需单击右上角的 X 图标即可关闭此信息框。

您还可以通过导航到Web控制台的管理员视图下的工作负载→Pods来获取项目中创建的所有Pods的列表。

Pod list

此Pod包含一个容器,parksmap应用程序——一个简单的Spring Boot/Java应用程序。

您还可以从命令行检查Pod:

oc get pods

您应该会看到类似于以下内容的输出:

NAME                READY   STATUS      RESTARTS   AGE
parksmap-65c4f8b676-k5gkk    1/1     Running     0          20s

上述输出列出了当前项目中的所有Pod,包括Pod名称、状态、重新启动和正常运行时间。

获得Pod名称后,您可以使用oc get命令获取有关Pod的更多信息。为了使输出可读,我建议使用以下语法将输出类型更改为YAML:

确保您使用了刚刚屏幕输出中的正确Pod名称。
oc get pod parksmap-1-gxbgq -o yaml

您应该会看到类似于以下输出的内容(由于本实验手册的篇幅原因已被截断):

apiVersion: v1
kind: Pod
metadata:
  annotations:
    k8s.v1.cni.cncf.io/network-status: |-
      [{
          "name": "",
          "interface": "eth0",
          "ips": [
              "10.131.0.93"
          ],
          "default": true,
          "dns": {}
      }]
    k8s.v1.cni.cncf.io/networks-status: |-
      [{
          "name": "",
          "interface": "eth0",
          "ips": [
              "10.131.0.93"
          ],
          "default": true,
          "dns": {}
      }]
    openshift.io/generated-by: OpenShiftWebConsole
    openshift.io/scc: restricted
  creationTimestamp: "2021-01-05T17:00:32Z"
  generateName: parksmap-65c4f8b676-
  labels:
    app: parksmap
    component: parksmap
    deploymentconfig: parksmap
    pod-template-hash: 65c4f8b676
    role: frontend
...............

Web 界面还在Pod详细信息页面上显示了许多相同的信息。如果您点击Pod的名称,您将找到详细信息页面。您还可以通过单击拓扑 Topology 页面上的parksmap部署配置,选择 资源 Resources,然后单击Pod名称来到达那里。

Parksmap Resources

从这里您可以看到配置、指标、环境变量、日志、事件,并可以在正在运行的 pod 上获取终端 shell。

Pod Details
Pod Events

获取parksmap 镜像运行,可能需要一段时间才能完成。如果节点尚未在本地缓存它,则要求运行该映像的每个 OpenShift 节点都必须拉取(下载)它。您可以在Pod详细信息页面中查看映像下载和部署的状态,也可以使用之前使用的命令oc get pods从命令行查看。

Developer控制台中的默认视图是Graph View。您可以使用控制台右上角的切换按钮在图表和列表视图之间切换。

List View Toggle
Topology View Toggle

背景:自定义镜像生命周期行为

每当 OpenShift 要求节点的 CRI(容器运行时接口)运行时(Docker 守护程序或 CRI-O)运行映像时,运行时都会检查以确保它具有要运行的正确“版本”映像。如果没有,它将从指定的镜像仓库中提取它。

有多种方法可以自定义此行为。相关文档在 指定镜像镜像拉取策略.

背景: 服务 Services

服务在 OpenShift 内部提供了一个方便的抽象层来查找一组相似的Pod。它们还充当这些Pod与需要从 OpenShift 环境内部访问它们的任何其他内容之间的内部代理/负载平衡器。例如,如果您需要更多parksmap实例来处理负载,则可以启动更多Pod。OpenShift 自动将它们作为端点映射到Service,传入的请求不会注意到任何不同.

当您要求 OpenShift 运行映像时,它会自动 为您创建一个服务。请记住,服务是一种内部构造。它们不可用于“外部世界”或 OpenShift 环境之外的任何内容。没关系,稍后您将了解。

服务映射一组POD是通过标签*Labels*和选择器*Selectors*。服务被分配了一个固定的 IP 地址,并且可以映射许多端口和协议。

更多的关于服务的信息,包括手动通过YAML格式创建服务,请参考官方文档 Services

在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机器的 IP 地址。但是在 K8s 集群里面应用是通过 pod 去部署的, 而 pod 生命周期是短暂的。在 pod 的生命周期过程中,比如它创建或销毁,它的 IP 地址都会发生变化,这样就不能使用传统的部署方式,不能指定 IP 去访问指定的应用。 在 K8s 里面,服务发现与负载均衡就是 K8s Service。K8s Service 向上提供了外部网络以及 pod 网络的访问,即外部网络可以通过 service 去访问,pod 网络也可以通过 K8s Service 去访问。集群内部可以通过service 的虚拟 IP,直接访问服务名(依靠 DNS 解析),通过环境变量访问三种方式访问services,当 pod 的生命周期有变化时,比如说其中一个 pod 销毁,service 就会自动从后端摘除这个 pod。这样实现了:就算 pod 的生命周期有变化,它访问的端点是不会发生变化的。扩展阅读

现在我们了解了Service的基础知识,让我们看一下为我们刚刚部署的映像创建的Service。要查看在您的项目中定义的服务,请输入以下命令:

oc get services

您应该会看到类似于以下内容的输出:

NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
parksmap   ClusterIP   172.30.22.209  <none>        8080/TCP   3h

在上面的输出,我们可以看到,我们有一个服务命名parksmap与172.30.22.209/8080TCP的IP /端口组合。您的 IP 地址可能不同,因为每项服务在创建时都会收到一个唯一的 IP 地址。服务IP 是固定的,在服务的生命周期内永远不会改变。

在来自Topology视图的 Developer 透视图中,通过单击parksmap部署配置,然后单击Resources可以获得服务信息,然后您应该会看到Services部分中的parksmap条目。

Services list

您还可以通过使用以下命令在 YAML 中显示数据来获取有关服务service的更多详细信息:

oc get service parksmap -o yaml

您应该会看到类似于以下内容的输出:

apiVersion: v1
kind: Service
metadata:
  annotations:
    openshift.io/generated-by: OpenShiftWebConsole
  creationTimestamp: "2020-09-30T14:10:12Z"
  labels:
    app: workshop
    app.kubernetes.io/component: parksmap
    app.kubernetes.io/instance: parksmap
    app.kubernetes.io/part-of: workshop
    component: parksmap
    role: frontend
  name: parksmap
  namespace: workshop
  resourceVersion: "1062269"
  selfLink: /api/v1/namespaces/workshop/services/parksmap
  uid: e1ff69c8-cb2f-11e9-82a1-0267eec7e1a0
spec:
  clusterIP: 172.30.22.209
  ports:
  - name: 8080-tcp
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: parksmap
    deploymentconfig: parksmap
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

请注意YAML中的`selector` 标签选择器, 记住它

或者,您可以使用 Web 控制台通过单击上一屏幕中的服务来查看有关服务的信息。

Service

查看Pod的 YAML以了解 OpenShift 如何将组件连接在一起也很有趣。例如,运行以下命令以获取parksmap Pod的名称:

oc get pods

您应该会看到类似于以下内容的输出:

NAME                        READY   STATUS    RESTARTS   AGE
parksmap-65c4f8b676-k5gkk   1/1     Running   0          5m12s

现在您可以使用以下命令查看Pod的详细数据:

oc get pod parksmap-1-gxbgq -o yaml

在metadata部分下,您应该看到以下内容:

  labels:
    app: parksmap
    deploymentconfig: parksmap
  • 服务使用标签选择器 selector引用`deploymentconfig=parksmap`.

  • POD有多个标签:

    • app=parksmap

    • deploymentconfig=parksmap

标签是键/值对。此项目中具有与Selector匹配的Label 的任何Pod都将与Service相关联。要查看此操作,请键入以下命令:

oc describe service parksmap

您应该会看到类似于以下输出的内容:

Name:              parksmap
Namespace:         workshop
Labels:            app=workshop
                   app.kubernetes.io/component=parksmap
                   app.kubernetes.io/instance=parksmap
                   app.kubernetes.io/part-of=workshop
                   component=parksmap
                   role=frontend
Annotations:       openshift.io/generated-by: OpenShiftWebConsole
Selector:          app=parksmap,deploymentconfig=parksmap
Type:              ClusterIP
IP:                172.30.22.209
Port:              8080-tcp  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.128.2.90:8080
Session Affinity:  None
Events:            <none>

您可能想知道为什么只列出了一个Endpoints。这是因为当前只有一个Pod正在运行。在下一个实验中,我们将学习如何扩展应用程序,此时您将能够看到与Service关联的多个Endpoints。

ClusterIP

集群内部的一个虚拟 IP,这个 IP 会绑定到一堆服务的 Group Pod 上面,这也是默认的服务方式。它的缺点是这种方式只能在 Node 内部也就是集群内部使用。

NodePort

供集群外部调用。将 Service 承载在 Node 的静态端口上,端口号和 Service 一一对应,那么集群外的用户就可以通过 <NodeIP>:<NodePort> 的方式调用到 Service。

LoadBalancer

给云厂商的扩展接口。像阿里云、亚马逊这样的云厂商都是有成熟的 LB 机制的,这些机制可能是由一个很大的集群实现的,为了不浪费这种能力,云厂商可通过这个接口进行扩展。它首先会自动创建 NodePort 和 ClusterIP 这两种机制,云厂商可以选择直接将 LB 挂到这两种机制上,或者两种都不用,直接把 Pod 的 RIP 挂到云厂商的 ELB 的后端也是可以的。

ExternalName

摈弃内部机制,依赖外部设施,比如某个用户特别强,他觉得我们提供的都没什么用,就是要自己实现,此时一个 Service 会和一个域名一一对应起来,整个负载均衡的工作都是外部实现的。