将容器映像部署到 Azure Kubernetes 服务

已完成

在本单元中,你将容器映像部署到 Azure Kubernetes 服务(AKS)。 使用 AKS,可以将 Kubernetes 群集配置为通过 部署以所需状态运行,这是向 Pod 和 ReplicaSet 提供声明性更新的过程。 此状态声明在清单 YAML 文件中管理,Kubernetes 控制器在指示时将当前状态更改为声明的状态。 创建此 deployment.yml 清单文件,并指示 AKS 实例以所需状态运行,并将 Pod 配置为拉取并运行 flightbookingsystemsample 在上一单元中推送的 Azure 容器注册表中包含的容器映像。 此清单以声明方式定义部署和服务。 Kubernetes 使用部署控制器来管理应用程序的 Pod;还可以使用 命令式地创建 Deployment。

注释

如果会话已空闲,或者正在另一时间点或从另一个 CLI 执行此步骤,则可能需要使用以下命令重新初始化环境变量并重新进行身份验证:

还原安装程序中使用的资源变量值。 将下面的 <your-subscription-ID> 替换为你用于此模块的订阅 ID。 如果登录提供订阅选择器,请选择同一订阅。 一次运行一个身份验证命令。 在 az account show 之后,确认当前订阅与设置匹配,然后再继续。 如果命令失败,请停止并解决错误。

export AZ_RESOURCE_GROUP=java-containerization-demo-rg
export AZ_CONTAINER_REGISTRY=<your-container-registry>
export AZ_KUBERNETES_CLUSTER=java-containerization-demo-aks
export AZ_LOCATION=<your-Azure-region>
export AZ_KUBERNETES_CLUSTER_DNS_PREFIX=<your-unique-DNS-prefix-to-access-your-AKS-cluster>
az login
az account set --subscription "<your-subscription-ID>"
az account show --output table
az acr login --name "$AZ_CONTAINER_REGISTRY"

部署容器映像

现在,将 flightbookingsystemsample 容器映像部署到 AKS 群集。 在 containerize-and-deploy-Java-app-to-Azure/Project/Airlines中,使用已安装的文本编辑器创建名为 deployment.yml的文件。 此路径相对于克隆存储库所在的父目录。 如果已进入 Project/Airlines,请留在那里,不要再次切换到完整的相对路径。 创建文件,然后从此目录运行后面的 kubectl apply -f deployment.yml 命令。

将以下内容添加到 deployment.yml,然后保存该文件。 将 <AZ_CONTAINER_REGISTRY> 占位符替换为之前设置的 AZ_CONTAINER_REGISTRY 环境变量的值。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flightbookingsystemsample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flightbookingsystemsample
  template:
    metadata:
      labels:
        app: flightbookingsystemsample
    spec:
      nodeSelector:
        kubernetes.io/os: linux
        kubernetes.io/arch: amd64
      containers:
      - name: flightbookingsystemsample
        image: <AZ_CONTAINER_REGISTRY>.azurecr.io/flightbookingsystemsample:latest
        resources:
          requests:
            cpu: "1"
            memory: "1Gi"
          limits:
            cpu: "2"
            memory: "2Gi"
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: flightbookingsystemsample
spec:
  type: LoadBalancer
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: flightbookingsystemsample

注释

如果使用项目根文件夹中的 deployment_solution.yml 文件作为起点,请将其映像更新为与此处所示的注册表映像和 :latest 标记相同。 同时在部署的containers下添加相同的 Linux AMD64spec.template.spec,与nodeSelector并列。 请勿原样使用该替代项:其映像和节点选择器必须与此模块的构建平台相匹配。

在前面的 deployment.yml中,该文件包含部署和服务。 部署用于管理一组 Pod,服务用于允许网络访问这些 Pod。 Pod 已配置为从 Azure 容器注册表中拉取一个映像 <AZ_CONTAINER_REGISTRY>.azurecr.io/flightbookingsystemsample:latest。 该服务配置为允许传入到端口 8080 的 HTTP Pod 流量,类似于使用 -p 端口参数在本地运行容器映像的方式。

节点选择器将 Pod 限制在与前面构建的 linux/amd64 映像相匹配的 Linux AMD64 节点上。 它属于 Deployment,而不是 Service。 选择器限制调度;它不会使不兼容的映像在另一个体系结构上运行。

在配置群集访问之前,请确保在安装单元中创建 AKS 群集成功完成。 检查其预配状态和当前 Kubernetes 版本:

az aks show \
    --resource-group "$AZ_RESOURCE_GROUP" \
    --name "$AZ_KUBERNETES_CLUSTER" \
    --query '{provisioningState:provisioningState,currentKubernetesVersion:currentKubernetesVersion}' \
    --output table

如果命令失败、provisioningState 不是 Succeeded,或者 currentKubernetesVersion 不是 major.minor.patch 格式的非空版本,请不要继续。

以下工具说明适用于在设置中建立的本地 Linux、macOS 或 WSL Bash 会话,而不是本机 PowerShell 或命令提示符。 如果在该会话中已存在可正常工作的 kubectl 和 kubelogin 安装,请保留它们,而不是重新安装。 请使用kubectl version --client和kubelogin --version检查它们,并确保kubectl满足该群集的受支持的版本偏差策略。

否则,请用返回的 currentKubernetesVersion 替换 <control-plane-version>。 将匹配的 kubectl 版本和 kubelogin 安装到用户可写位置:

KUBECTL_VERSION="<control-plane-version>"
if [[ "$KUBECTL_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    mkdir -p "$HOME/.local/bin" &&
    az aks install-cli \
        --client-version "$KUBECTL_VERSION" \
        --install-location "$HOME/.local/bin/kubectl" \
        --kubelogin-install-location "$HOME/.local/bin/kubelogin" &&
    export PATH="$HOME/.local/bin:$PATH" &&
    kubectl version --client &&
    kubelogin --version
else
    printf 'Stop: set KUBECTL_VERSION to the returned major.minor.patch version.\n' >&2
    false
fi

这两个安装路径都包含可执行文件文件名。 导出 PATH 仅适用于当前 shell;在新会话中重复导出,或将其添加到相应的 Bash 启动配置。 在继续之前解决任何安装或版本检查失败。

两种工具都可用并验证后,请配置 kubectl 以连接到您的 AKS 群集:

az aks get-credentials \
    --resource-group $AZ_RESOURCE_GROUP \
    --name $AZ_KUBERNETES_CLUSTER

以下输出是典型的:

Merged AZ_KUBERNETES_CLUSTER as current context in ~/.kube/config

在应用清单之前,请检查实际的节点操作系统和体系结构:

kubectl get nodes \
    -o custom-columns='NAME:.metadata.name,OS:.status.nodeInfo.operatingSystem,ARCH:.status.nodeInfo.architecture'

此模块的 linux/amd64 构建路径需要 Linux AMD64 节点。 如果没有匹配的节点,请在继续操作之前停止并解决不匹配。 使用支持的 AMD64 节点池,或为另一种支持的 Linux 节点架构重新构建、运行并推送映像,并更新部署的架构选择器以与之匹配。 Docker --platform 选项不会创建或更改 AKS 节点池。

从 containerize-and-deploy-Java-app-to-Azure/Project/Airlines 中,将 deployment.yml 应用于群集:

kubectl apply -f deployment.yml

以下输出是典型的:

deployment.apps/flightbookingsystemsample created
service/flightbookingsystemsample created

使用以下命令监视部署的状态:

kubectl get all

以下输出是典型的:

kubectl get all
NAME                                            READY   STATUS    RESTARTS   AGE
pod/flightbookingsystemsample-b7fd6684f-4j8vf   1/1     Running   0          37s

NAME                                TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)          AGE
service/flightbookingsystemsample   LoadBalancer   10.0.62.153   <external-IP>   8080:30534/TCP   37s
service/kubernetes                  ClusterIP      10.0.0.1      <none>          443/TCP          11h

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/flightbookingsystemsample   1/1     1            1           37s

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/flightbookingsystemsample-b7fd6684f   1         1         1       37s

Pod Running 的状态并不能确认 Tomcat 已完成应用程序部署或 HTTP 请求是否成功。 等待部署滚动发布,然后跟踪应用程序启动日志:

kubectl rollout status deployment/flightbookingsystemsample --timeout=300s &&
kubectl logs deployment/flightbookingsystemsample --tail=100 --follow

成功的部署会检查 Kubernetes 可用性。 由于此清单没有应用程序运行状况探测,所以这次推出并非 HTTP 运行状况检查。 查看日志,直到 Tomcat 报告服务器启动已完成,并且部署 AirlinesReservationSample.war 已完成且没有错误。 然后按 Ctrl+C 停止关注日志。 这只会停止日志流,而不是容器。 如果部署或应用程序启动失败或未完成,请检查日志和 Pod 事件,而不是继续执行浏览器检查。

以下历史输出演示了早期 Tomcat 10.1.33 镜像中的启动消息。 这不是此模块中更新后的映像输出的内容;使用自己的日志来验证启动过程,而不是期待相同的版本、时间戳或环境详细信息:

kubectl logs pod/flightbookingsystemsample-b7fd6684f-4j8vf
15-Nov-2024 12:31:51.208 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name:   Apache Tomcat/10.1.33
15-Nov-2024 12:31:51.211 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Nov 7 2024 22:28:55 UTC
15-Nov-2024 12:31:51.212 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 10.1.33.0
15-Nov-2024 12:31:51.212 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
15-Nov-2024 12:31:51.212 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            5.15.0-1074-azure
15-Nov-2024 12:31:51.212 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
15-Nov-2024 12:31:51.216 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /usr/lib/jvm/msopenjdk-17
15-Nov-2024 12:31:51.216 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           17.0.13+11-LTS
15-Nov-2024 12:31:51.216 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor:            Microsoft
15-Nov-2024 12:31:51.216 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE:         /usr/local/tomcat
15-Nov-2024 12:31:51.216 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME:         /usr/local/tomcat
15-Nov-2024 12:31:51.226 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
15-Nov-2024 12:31:51.226 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
15-Nov-2024 12:31:51.227 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
15-Nov-2024 12:31:51.227 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
15-Nov-2024 12:31:51.227 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027
15-Nov-2024 12:31:51.227 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED
15-Nov-2024 12:31:51.228 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.io=ALL-UNNAMED
15-Nov-2024 12:31:51.228 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util=ALL-UNNAMED
15-Nov-2024 12:31:51.228 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED
15-Nov-2024 12:31:51.229 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
15-Nov-2024 12:31:51.229 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
15-Nov-2024 12:31:51.229 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
15-Nov-2024 12:31:51.229 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
15-Nov-2024 12:31:51.231 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent The Apache Tomcat Native library which allows using OpenSSL was not found on the java.library.path: [/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib]
15-Nov-2024 12:31:51.565 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
15-Nov-2024 12:31:51.602 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [616] milliseconds
15-Nov-2024 12:31:51.658 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
15-Nov-2024 12:31:51.659 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.1.33]
15-Nov-2024 12:31:51.689 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/AirlinesReservationSample.war]
15-Nov-2024 12:31:53.264 INFO [main] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
15-Nov-2024 12:31:55.155 INFO [main] com.sun.xml.ws.server.MonitorBase.createRoot Metro monitoring rootname successfully set to: com.sun.metro:pp=/,type=WSEndpoint,name=/AirlinesReservationSample-PriceAndSeats-PriceAndSeatsPort
15-Nov-2024 12:31:55.347 INFO [main] com.sun.xml.ws.transport.http.servlet.WSServletDelegate.<init> WSSERVLET14: XML-WS servlet initializing
WARNING:Error in the metadata of the table SEATS: table's row count in the metadata is 5 but 6 records have been found and loaded by UCanAccess. All will work fine, but it's better to repair your database.
15-Nov-2024 12:31:56.248 INFO [main] com.sun.xml.ws.transport.http.servlet.WSServletContextListener.contextInitialized WSSERVLET12: XML-WS context listener initializing
15-Nov-2024 12:31:56.249 INFO [main] com.sun.xml.ws.transport.http.servlet.WSServletContextListener.contextInitialized WSSERVLET12: XML-WS context listener initializing
15-Nov-2024 12:31:56.284 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/AirlinesReservationSample.war] has finished in [4,595] ms
15-Nov-2024 12:31:56.285 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/docs]
15-Nov-2024 12:31:56.315 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/docs] has finished in [30] ms
15-Nov-2024 12:31:56.316 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/examples]
15-Nov-2024 12:31:56.439 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/examples] has finished in [123] ms
15-Nov-2024 12:31:56.440 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/manager]
15-Nov-2024 12:31:56.456 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/manager] has finished in [16] ms
15-Nov-2024 12:31:56.457 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/ROOT]
15-Nov-2024 12:31:56.468 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/ROOT] has finished in [11] ms
15-Nov-2024 12:31:56.468 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/host-manager]
15-Nov-2024 12:31:56.481 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/host-manager] has finished in [13] ms
15-Nov-2024 12:31:56.484 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
15-Nov-2024 12:31:56.501 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [4897] milliseconds

监视服务的外部地址:

kubectl get service flightbookingsystemsample --watch

该值EXTERNAL-IP最初可以是<pending>,而此时 Azure 正在预配负载均衡器。 这可能需要几分钟时间,这本身并不表示部署失败。 出现地址时,按 Ctrl+C 停止查看。 如果预配未完成,请停止监视,并使用 kubectl describe service flightbookingsystemsample 检查服务事件。

以下说明输出显示了分配的地址的显示位置:

NAME                        TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)          AGE
flightbookingsystemsample   LoadBalancer   10.0.62.153   <external-IP>   8080:30534/TCP   3m28s

注释

将以下 URL 中的 IP 地址 <external-IP> 替换为之前执行的命令中获得的 EXTERNAL-IP 值。

打开浏览器并访问航班预订系统示例登录页面 http://<external-IP>:8080/AirlinesReservationSample/。 确认应用页面有响应;仅有 Pod Running 或分配的 IP 并不能证明应用程序已就绪。 您的浏览器应与以下截图类似:

在浏览器中的Azure Kubernetes 服务上运行的航班预订系统登陆页面的屏幕截图。

可以选择使用 来自tomcat-users.xml 的任何用户登录 ,例如 someuser@azure.com: password。

排查映像平台不匹配问题

对于 Pod 启动失败,请检查kubectl describe pods -l app=flightbookingsystemsample、kubectl logs deployment/flightbookingsystemsample --tail=100和kubectl get events --sort-by=.metadata.creationTimestamp。 CrashLoopBackOff 状态可能由多种原因引起,包括应用程序错误;不要假设每次失败都是体系结构不匹配。

如果确认已部署的映像平台有误,请先使用匹配的平台设置重复执行生成、本地运行、映像检查、注册表标记和推送步骤。 成功重新生成、重新标记并推送到同一 :latest 标记下更正的映像后,显式替换正在运行的 Pod:

kubectl rollout restart deployment/flightbookingsystemsample &&
kubectl rollout status deployment/flightbookingsystemsample --timeout=300s

对于此清单,:latest 默认为 imagePullPolicy: Always,因此替换 Pod 会解析当前注册表标记。 仅重新应用未更改的清单不会触发部署。 如果自定义了拉取策略,请使用更新后的映像标记或摘要,并改为更新部署。 在部署后重复启动日志和应用程序 URL 检查。

清理资源

在此模块中创建的资源可能会产生持续成本。 若要清理资源,请使用以下命令删除包含这些资源的资源组:

az group delete --name $AZ_RESOURCE_GROUP