日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

推送到 Nexus 仓库->部署为 k8s 服务实践">构建 dotnet&vue 应用镜像->推送到 Nexus 仓库->部署为 k8s 服务实践

發布時間:2024/1/2 vue 42 coder
生活随笔 收集整理的這篇文章主要介紹了 推送到 Nexus 仓库->部署为 k8s 服务实践">构建 dotnet&vue 应用镜像->推送到 Nexus 仓库->部署为 k8s 服务实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

前面分享了 k8s 的部署安裝,本篇來點實操,將會把一個 .net core + vue 的項目(zhontai),打包構建成 docker 鏡像,推送到 nexus 鏡像倉庫,并部署到 k8s 中

準備

要實現項目的部署,除了準備要部署的環境(k8s),還需要準備項目所用到的各中間件,本文旨在分享部署的一個整體流程,對項目中所使用到的各中間件(mysql,redis 等)的安裝使用可自行在本 DevOps 系列文章中找到

  • 一個 .net core+vue 的項目

    • 使用 zhontai 項目,之前也有做分享,文章介紹
    • 后端 Admin.Core v3.7.1
    • 前端 admin.ui.plus v2.2.0
  • Nexus 的安裝部署,文章介紹

    • 做為鏡像倉庫使用,將項目打包鏡像及項目鏡像推送到倉庫,k8s 也從此倉庫拉取鏡像
    • 版本為 v3.61 ,安裝地址為 192.168.0.214:8081,并使用局域網域名解析,
    • 在目標機器先登錄能夠拉取推送鏡像,參考
    • 拉取鏡像地址:https://nexus.devops.test.com
    • 推送鏡像地址:https://push.nexus.devops.test.com
  • Docker 的安裝部署,文章介紹

    • 使用 doker 拉取 sdk、nodejs 鏡像進行打包,構建 k8s 所需要的項目鏡像
    • 版本:v24.0.6
  • K8S 的安裝與部署,文章介紹

    • 部署項目服務
    • 使用 ingress 解析域名到服務
  • 部署前后端項目到 K8S,本文介紹

使用 Docker 打包應用鏡像

不管什么語言,基本都可以使用這個打包流程,將官方鏡像打包推送到私有鏡像倉庫個人認為是必要的,不然如果一旦遠端的鏡像失效,又需要重新拉取鏡像時就會很尬尷。

  1. 準備打包所需鏡像

    1. 獲取基礎打包鏡像(dotnet 獲取 sdk 鏡像,vue 獲取 node 鏡像)
    2. 基于基礎鏡像,安裝所需軟件,設置默認配置,復制默認文件,封裝項目的打包鏡像
    3. 掛載項目到 sdk 鏡像進行打包,打包后獲取構建完成的產物
  2. 準備運行所需的基礎鏡像

    1. 獲取運行時鏡像(.net core 獲取 runtime 鏡像,vue 獲取 nginx 鏡像)
    2. 基于運行時鏡像,將打包構建完從的產物添加到鏡像,構建項目鏡像
    3. 推送項目鏡像到倉庫

.Net Core 7.0 項目鏡像

構建所需一個 sdk 鏡像用于打包編譯項目,一個 runtime 鏡像運行 .net core 項目,版本選擇對應的 7.0 即可

  • dotnet sdk 鏡像地址:https://hub.docker.com/_/microsoft-dotnet-sdk/
  • dotnet runtime 鏡像地址: https://hub.docker.com/_/microsoft-dotnet-runtime/

構建 dotnet sdk 7.0 打包鏡像

  1. 拉取 dotnet sdk 鏡像: docker pull mcr.microsoft.com/dotnet/sdk:7.0

    1. 目前可以直接拉取,若無法拉取則配置國內鏡像源
    2. 臨時運行容器進行測試: docker run -it --rm mcr.microsoft.com/dotnet/sdk:7.0,可以將需要的東西進行安裝測試再編寫 dockerfile
  2. 使用 Dockerfile 構建打包鏡像 dotnet-sdk-7.0

    1. 為了便于后期維護,使用 Dockerfile 來構建
    2. 目錄文件:dotnet-sdk-7.0/Dockerfile
    3. 基于 sdk 安裝 dotnet-monitor v7.3.2, 文檔 這里只做演示,暫時沒用上,后續使用多階段構建的時候可以將其復制到運行時鏡像中
    4. # 基礎sdk鏡像 v7.0    
      FROM mcr.microsoft.com/dotnet/sdk:7.0
      # 將tools目錄加入環境變量
      ENV PATH="$PATH:/root/.dotnet/tools"
      # 安裝 dotnet-monitor 
      RUN dotnet tool install -g dotnet-monitor --version 7.3.2
      
    5. 執行構建:docker build -t dotnet-sdk-7.0 -f ./Dockerfile .
  3. 推送鏡像到 Nexus 倉庫

    1. 鏡像登錄認證:docker login push.nexus.devops.test.com -u pusher -p devops666
    2. 打標簽: docker tag dotnet-sdk-7.0 push.nexus.devops.test.com/projectbuild/dotnet-sdk-7.0
    3. 推送鏡像: docker push push.nexus.devops.test.com/projectbuild/dotnet-sdk-7.0
    4. 記得清理本地緩存鏡像: docker rmi dotnet-sdk-7.0 && docker rmi push.nexus.devops.test.com/projectbuild/dotnet-sdk-7.0
  4. 使用鏡像

    1. 后續使用 dotnet sdk 7.0 就可以直接使用 nexus.devops.test.com/projectbuild/dotnet-sdk-7.0 即可
    2. 直接拉取: docker pull nexus.devops.test.com/projectbuild/dotnet-sdk-7.0

構建 dotnet runtime 7.0 運行時鏡像

  1. 拉取 dotnet runtime 鏡像: docker pull mcr.microsoft.com/dotnet/runtime:7.0

    1. 臨時運行容器進行測試: docker run -it --rm mcr.microsoft.com/dotnet/runtime:7.0
  2. 使用 Dockerfile 構建運行時鏡像

    1. 為了便于后期維護,使用 Dockerfile 來構建
    2. 目錄文件:dotnet-runtime-7.0/Dockerfile
    3. 若非需要,可以不安裝軟件,安裝軟件后鏡像會多個 100M
    4. # 基礎runtime鏡像 v7.0   
      FROM mcr.microsoft.com/dotnet/aspnet:7.0
      
      # 寫入阿里云鏡像源
      RUN echo " \
      deb http://mirrors.aliyun.com/debian/ bullseye main contrib non-free\n \
      deb-src http://mirrors.aliyun.com/debian/ bullseye main contrib non-free\n \
      \n \
      deb http://mirrors.aliyun.com/debian-security bullseye-security main contrib non-free\n \
      deb-src http://mirrors.aliyun.com/debian-security bullseye-security main contrib non-free\n \
      \n \
      deb http://mirrors.aliyun.com/debian/ bullseye-updates main contrib non-free\n \
      deb-src http://mirrors.aliyun.com/debian/ bullseye-updates main contrib non-free\n \
      \n \
      deb http://mirrors.aliyun.com/debian/ bullseye-backports main contrib non-free\n \
      deb-src http://mirrors.aliyun.com/debian/ bullseye-backports main contrib non-free\n \
      " | tee /etc/apt/sources.list
      
      # 安裝 curl&&vim
      RUN apt-get update -y && apt-get install -y curl && apt-get install -y vim
      
    5. 執行構建:docker build -t dotnet-runtime-7.0 -f ./Dockerfile .
  3. 推送鏡像到 Nexus 倉庫

    1. 鏡像登錄認證:docker login push.nexus.devops.test.com -u pusher -p devops666
    2. 打標簽:docker tag dotnet-runtime -7.0 push.nexus.devops.test.com/projectbuild/dotnet-runtime-7.0
    3. 推送鏡像: docker push push.nexus.devops.test.com/projectbuild/dotnet-runtime-7.0
    4. 記得清理本地緩存鏡像: docker rmi dotnet-runtime-7.0 && docker rmi push.nexus.devops.test.com/projectbuild/dotnet-runtime-7.0
  4. 使用鏡像

    1. 后續使用 dotnet runtime 7.0 就可以直接使用 nexus.devops.test.com/projectbuild/dotnet-runtime-7.0 即可
    2. 直接拉取: docker pull nexus.devops.test.com/projectbuild/dotnet-runtime-7.0

構建 zhontai 后端項目的應用鏡像

制作完鏡像,下面將使用 sdk 鏡像打包項目生成部署文件,再使用 runtime 鏡像部署運行。

  1. 下載/克隆項目 admin.core 到服務器,進入項目目錄開始執行

    1. # 克隆項目
      git clone https://github.com/zhontai/Admin.Core.git -b v3.7.0
      # 進入項目 cd Admin.Core
      cd Admin.Core
      
    2. src 為.net core 項目代碼
  2. 使用 sdk 鏡像進行打包,生成部署文件到 publish_output

    1. docker run -i --rm 創建一個臨時容器,容器退出后自動刪除容器
    2. -v ./build:/build 掛載 MSBuild 屬性文件目錄(./src/Directory.Build.props 中使用)
    3. -v ./src:/src 掛載源碼到容器中
    4. -v ./publish_output:/publish_output掛載構建物輸出目錄
    5. --name build_zhontai_api 指定運行的容器名稱
    6. nexus.devops.test.com/projectbuild/dotnet-sdk-7.0 sdk 鏡像
    7. /bin/bash -c "xxx"以交互模式運行容器,運行時執行命令
    8. 若有自定義 nuget 倉庫的包還需掛載 /root/.nuget 目錄,或直接制作在鏡像中
    9. 記得掛載 build 目錄,否則會提示:Invalid framework identifier
    10. docker run -i --rm \
      -v ./build:/build \
      -v ./src:/src \
      -v ./publish_output:/publish_output \
      --name build_zhontai_api \
      nexus.devops.test.com/projectbuild/dotnet-sdk-7.0 \
      /bin/bash -c 'dotnet publish /src/hosts/ZhonTai.Host -c Release -o /publish_output --runtime linux-x64 --framework net7.0'
      
    11. 執行成功后程序包就生成到 publish_output 中了
  3. 使用 runtime 鏡像制作應用鏡像

    1. 將上一步的構建物 Admin.Core/publish_output 添加到運行時鏡像中
    2. 使用 echo 創建一個 Dockerfile
    3. #創建Dockerfile
      echo 'FROM nexus.devops.test.com/projectbuild/dotnet-runtime-7.0 AS runtime 
      WORKDIR /app 
      COPY ./publish_output /app 
      ENV ASPNETCORE_URLS=http://+:8000 
      ENTRYPOINT ["dotnet", "ZhonTai.Host.dll"]' > Dockerfile
      
    4. 執行構建:docker build -t zhontai_api .
    5. 運行測試,成功
  4. 推送鏡像到倉庫

    1. #打標簽
      docker tag zhontai_api push.nexus.devops.test.com/projectapp/zhontai_api
      #推送
      docker push push.nexus.devops.test.com/projectapp/zhontai_api
      
    2. 推送成功,這里手動只構建的 latest 版本,若使用自動化構建,還需構建對應版本的鏡像,以支持快速回滾

Vue 3 項目打包

構建所需一個 node 鏡像用于 vue 項目打包,nginx 用于部署前臺項目

  1. node 鏡像地址:https://hub.docker.com/r/library/node ,選擇版本:node:18.17.1
  2. nginx 鏡像地址:https://hub.docker.com/_/nginx ,選擇版本:nginx:1.24.0

構建 nodejs 18.17.1 打包鏡像

  1. 拉取 nodejs 鏡像:docker pull node:18.17.1

  2. 將 node 鏡像 vue-node-18 打上標簽推送到倉庫

    1. #拉取倉庫
      docker pull node:18.17.1
      # 打標簽
      docker tag node:18.17.1 push.nexus.devops.test.com/projectbuild/vue-node-18.17
      #推送
      docker push push.nexus.devops.test.com/projectbuild/vue-node-18.17
      
    2. 測試使用: docker run -it --rm nexus.devops.test.com/projectbuild/vue-node-18.17 /bin/bash -c "node -v"

構建 nginx 1.24 運行時鏡像

  1. 拉取 nginx 鏡像:docker pull nginx:1.24

  2. 將 nginx 鏡像 vue-nginx-1.24 打上標簽推送到倉庫

    1. #拉取倉庫
      docker pull nginx:1.24
      # 打標簽
      docker tag nginx:1.24 push.nexus.devops.test.com/projectbuild/vue-nginx-1.24
      #推送
      docker push push.nexus.devops.test.com/projectbuild/vue-nginx-1.24
      
    2. 測試使用: docker run -it --rm nexus.devops.test.com/projectbuild/vue-nginx-1.24 /bin/bash進入容器后啟用 nginx,并使用 curl http://localhost 測試 nginx 可用

構建 zhontai 前端項目的應用鏡像

  1. 下載/克隆項目 admin.ui.plus 到文件夾

    1. # 克隆項目
      git clone https://github.com/zhontai/admin.ui.plus.git -b v2.2.0
      # 進入項目cd admin.ui.plus
      cd admin.ui.plus
      
      # 修改接口地址 
      # 編輯.env.production 中的 VITE_API_URL 配置為接口地址
      
  2. 使用 node 鏡像進行打包,生成文件到 dist

    1. docker run -i --rm \
      -v ./:/app \
      --name build_zhontai_webui \
      nexus.devops.test.com/projectbuild/vue-node-18.17 \
      /bin/bash -c 'cd /app 
      npm config set registry https://registry.npmmirror.com
      npm install
      npm run build'
      
    2. 執行成功,構建輸出到 dist 中
  3. 使用 nginx 鏡像制作應用鏡像

    1. # 創建nginx.conf
      
      echo '
      server {
          listen       80;
          server_name  localhost;
          charset utf-8;
          location / {
              root   /usr/share/nginx/html;
              try_files $uri $uri/ /index.html;
              index  index.html index.htm;
          }
      
          #error_page  404              /404.html;
          error_page   500 502 503 504  /50x.html;
          location = /50x.html {
              root   html;
          }
      }
      '> vue-nginx.conf
      #創建Dockerfile
      echo '
      FROM nexus.devops.test.com/projectbuild/vue-nginx-1.24
      EXPOSE 80
      COPY ./dist /usr/share/nginx/html
      COPY ./vue-nginx.conf /etc/nginx/conf.d/default.conf' > Dockerfile
      
    2. 執行構建:docker build -t zhontai_webui .
    3. 測試訪問成功
  4. 推送鏡像到倉庫

#打標簽
docker tag zhontai_webui push.nexus.devops.test.com/projectapp/zhontai_webui
#推送
docker push push.nexus.devops.test.com/projectapp/zhontai_webui

將 Docker 應用鏡像部署到 K8S

應用鏡像打包成功,現在需要將兩個應用精選鏡像部署到 k8s 中

應用鏡像的拉取憑證設置

因為 nexus 部署在局域網,并且配置的域名是局域網域名,所以面臨著如何在 k8s 中訪問 https://nexus.devops.test.com 獲取鏡像的問題,目前我的解決方法時每個節點機器都配置好對應 dns

要想訪問到 nexus 倉庫,需要滿足兩個條件,一個是訪問到倉庫,一個是倉庫的認證

  1. 給 k8s 所有節點添加 dns 設置nameserver 192.168.123.214

    1. 使用 docker login nexus.devops.test.com -u puller -p devops666 在宿主機中登錄倉庫確保可以在節點拉取鏡像
  2. 創建 nexus 登錄憑證

    1. kubectl create secret \
      docker-registry \
      nexus-login-registry \
      --docker-server=nexus.devops.test.com \
      --docker-username=puller \
      --docker-password=devops666 \
      -n default
      

使用 Deployment 部署應用

配置僅供參考,關于數據庫,配置文件,日志,上傳文件等未處理

  • 創建后端部署配置:zhontai_api.yaml

    • ---
      ## 配置服務
      kind: Service
      apiVersion: v1
      metadata:
        name: app-zhontai-api
        namespace: default
        labels:
          app: app-zhontai-api
      spec:
        selector:
          app: app-zhontai-api
        type: ClusterIP
        ports:
          - name: p80
            port: 80
            targetPort: 8000
      ---
      
      kind: Deployment # 指定創建資源的角色/類型
      apiVersion: apps/v1 # 指定api版本,此值必須在kubectl api-versions中
      metadata: # 資源的元數據/屬性
        name: app-zhontai-api # 資源的名字,在同一個namespace中必須唯一
        namespace: default # 部署在哪個namespace中
        labels: # 設定資源的標簽
          app: app-zhontai-api
      spec: # 資源規范字段  
        selector:
          matchLabels:
            app: app-zhontai-api
        replicas: 2 # 聲明副本數目
        revisionHistoryLimit: 2 # 保留歷史版本
        strategy: # 策略
          rollingUpdate: # 滾動更新
            maxSurge: 1 # 最大額外可以存在的副本數,可以為百分比,也可以為整數
            maxUnavailable: 1 # 示在更新過程中能夠進入不可用狀態的 Pod 的最大值,可以為百分比,也可以為整數
          type: RollingUpdate # 滾動更新策略
        template: # 模版
          metadata: # 資源的元數據/屬性
            labels: # 設定資源的標簽
              app: app-zhontai-api
          spec: # 資源規范字段
            containers:
              - image: nexus.devops.test.com/projectapp/zhontai_api:latest # 容器使用的鏡像地址
                name: app-zhontai-api # 容器的名字
                # 每次Pod啟動拉取鏡像策略,三個選擇 Always、Never、IfNotPresent
                # Always,每次都檢查;Never,每次都不檢查(不管本地是否有);IfNotPresent,如果本地有就不檢查,如果沒有就拉取
                imagePullPolicy: Always 
                resources: # 資源管理
                  # limits: # 最大使用
                  #   cpu: 300m # CPU,1核心 = 1000m
                  #   memory: 500Mi # 內存,1G = 1024Mi
                  # requests: # 容器運行時,最低資源需求,也就是說最少需要多少資源容器才能正常運行
                  #   cpu: 100m
                  #   memory: 100Mi
                livenessProbe: # pod 內部健康檢查的設置
                  httpGet: # 通過httpget檢查健康,返回200-399之間,則認為容器正常
                    path: /admin/health # URI地址
                    port: 8000 # 端口
                    scheme: HTTP # 協議
                  initialDelaySeconds: 10 # 表明第一次檢測在容器啟動后多長時間后開始
                  timeoutSeconds: 5 # 檢測的超時時間
                  periodSeconds: 30 # 檢查間隔時間
                  successThreshold: 1 # 成功門檻
                  failureThreshold: 5 # 失敗門檻,連接失敗5次,pod殺掉,重啟一個新的pod
                ports:
                  - name: http # 名稱
                    containerPort: 80 # 容器開發對外的端口
                    protocol: TCP # 協議
                env:
                  # 時區
                  - name: TZ
                    value: Asia/Shanghai
                  # app name
                  - name: APP_NAME
                    value: app.zhontai.api
                # 掛載
                volumeMounts:
                  - name: app-logs
                    mountPath: /logs #容器中的路徑
            # 卷軸
            volumes:
              - name: app-logs
                hostPath: 
                  path: /app/logs #將日志存放在宿主機的路徑,需要在宿主機創建目錄
                  type: Directory
            #重啟策略
            restartPolicy: Always    
            imagePullSecrets: # 鏡像倉庫拉取密鑰
              - name: nexus-login-registry
      
  • 執行部署:kubectl apply -f zhontai_api.yaml

  • 創建前端部署配置:zhontai_webui.yaml

    • ---
      ## 配置服務
      kind: Service
      apiVersion: v1
      metadata:
        name: app-zhontai-webui
        namespace: default
        labels:
          app: app-zhontai-webui
      spec:
        selector:
          app: app-zhontai-webui
        type: ClusterIP
        ports:
          - name: p80
            port: 80
            targetPort: 80
      ---
      
      kind: Deployment # 指定創建資源的角色/類型
      apiVersion: apps/v1 # 指定api版本,此值必須在kubectl api-versions中
      metadata: # 資源的元數據/屬性
        name: app-zhontai-webui # 資源的名字,在同一個namespace中必須唯一
        namespace: default # 部署在哪個namespace中
        labels: # 設定資源的標簽
          app: app-zhontai-webui
      spec: # 資源規范字段  
        selector:
          matchLabels:
            app: app-zhontai-webui
        replicas: 2 # 聲明副本數目
        revisionHistoryLimit: 2 # 保留歷史版本
        strategy: # 策略
          rollingUpdate: # 滾動更新
            maxSurge: 1 # 最大額外可以存在的副本數,可以為百分比,也可以為整數
            maxUnavailable: 1 # 示在更新過程中能夠進入不可用狀態的 Pod 的最大值,可以為百分比,也可以為整數
          type: RollingUpdate # 滾動更新策略
        template: # 模版
          metadata: # 資源的元數據/屬性
            labels: # 設定資源的標簽
              app: app-zhontai-webui
          spec: # 資源規范字段
            containers:
              - image: nexus.devops.test.com/projectapp/zhontai_webui:latest # 容器使用的鏡像地址
                name: app-zhontai-webui # 容器的名字
                # 每次Pod啟動拉取鏡像策略,三個選擇 Always、Never、IfNotPresent
                # Always,每次都檢查;Never,每次都不檢查(不管本地是否有);IfNotPresent,如果本地有就不檢查,如果沒有就拉取
                imagePullPolicy: Always 
                resources: # 資源管理
                  # limits: # 最大使用
                  #   cpu: 300m # CPU,1核心 = 1000m
                  #   memory: 500Mi # 內存,1G = 1024Mi
                  # requests: # 容器運行時,最低資源需求,也就是說最少需要多少資源容器才能正常運行
                  #   cpu: 100m
                  #   memory: 100Mi
                livenessProbe: # pod 內部健康檢查的設置
                  httpGet: # 通過httpget檢查健康,返回200-399之間,則認為容器正常
                    path: / # URI地址
                    port: 80 # 端口
                    scheme: HTTP # 協議
                  initialDelaySeconds: 10 # 表明第一次檢測在容器啟動后多長時間后開始
                  timeoutSeconds: 5 # 檢測的超時時間
                  periodSeconds: 30 # 檢查間隔時間
                  successThreshold: 1 # 成功門檻
                  failureThreshold: 5 # 失敗門檻,連接失敗5次,pod殺掉,重啟一個新的pod
                ports:
                  - name: http # 名稱
                    containerPort: 80 # 容器開發對外的端口
                    protocol: TCP # 協議
                env:
                  # 時區
                  - name: TZ
                    value: Asia/Shanghai
                  # app name
                  - name: APP_NAME
                    value: app.zhontai.webui
            #重啟策略
            restartPolicy: Always    
            imagePullSecrets: # 鏡像倉庫拉取密鑰
              - name: nexus-login-registry
      
  • 執行部署:kubectl apply -f zhontai_webui.yaml

配置 Ingress 使用域名訪問

  • 部署成功后添加對應 ingress 配置,即可使用域名訪問

前端項目需要修改為對應的接口地址

確保綁定的域名正常解析到 k8s 節點,即可使用域名訪問了,我這里使用的 DnsServer 泛解析,故可以直接訪問,

至此,一步步將一個單體項目部署到了 k8s 中,僅供參考,實際如果時微服務,還設計到一些通用和環境的配置,后面再慢慢分享。

根據上面的步驟,后面分享將其整理成腳本,以便后續可以直接使用。

相關文檔

  • 相關文章

    • 一篇適合躺收藏夾的 Nexus3 搭建 NuGet&Docker 私有庫的安裝使用總結
    • 一篇可供參考的 K8S 落地實踐經驗
    • 前后端都用得上的 Nginx 日常使用經驗
  • 參考文章

    • 如何創建一個帶診斷工具的.net 鏡像

后語

本文始于2023末,結束于2024始。

2023的最后兩個月,是這幾年以來,學習,產出最高的的兩個月。

始于國慶,不止步于元旦。

新年快樂!

總結

以上是生活随笔為你收集整理的推送到 Nexus 仓库->部署为 k8s 服务实践">构建 dotnet&vue 应用镜像->推送到 Nexus 仓库->部署为 k8s 服务实践的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。