当前位置: 首页 > news >正文

K8s(健康检查+滚动更新+优雅停机+弹性伸缩+Prometheus监控+配置分离)

前言

快速配置请直接跳转至汇总配置

K8s + SpringBoot实现零宕机发布:健康检查+滚动更新+优雅停机+弹性伸缩+Prometheus监控+配置分离(镜像复用)
配置
健康检查

健康检查类型:就绪探针(readiness)+ 存活探针(liveness)探针类型:exec(进入容器执行脚本)、tcpSocket(探测端口)、httpGet(调用接口)

业务层面

项目依赖 pom.xml
 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>

定义访问端口、路径及权限 application.yaml

management:server:port: 50000                         # 启用独立运维端口endpoint:                             # 开启health端点health:probes:enabled: trueendpoints:web:exposure:base-path: /actuator            # 指定上下文路径,启用相应端点include: health

将暴露/actuator/health/readiness和/actuator/health/liveness两个接口,访问方式如下:

http://127.0.0.1:50000/actuator/health/readiness
http://127.0.0.1:50000/actuator/health/liveness

运维层面

k8s部署模版deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:template:spec:containers:- name: {APP_NAME}image: {IMAGE_URL}imagePullPolicy: Alwaysports:- containerPort: {APP_PORT}- name: management-portcontainerPort: 50000         # 应用管理端口readinessProbe:                # 就绪探针httpGet:path: /actuator/health/readinessport: management-portinitialDelaySeconds: 30      # 延迟加载时间periodSeconds: 10            # 重试时间间隔timeoutSeconds: 1            # 超时时间设置successThreshold: 1          # 健康阈值failureThreshold: 6          # 不健康阈值livenessProbe:                 # 存活探针httpGet:path: /actuator/health/livenessport: management-portinitialDelaySeconds: 30      # 延迟加载时间periodSeconds: 10            # 重试时间间隔timeoutSeconds: 1            # 超时时间设置successThreshold: 1          # 健康阈值failureThreshold: 6          # 不健康阈值

滚动更新

k8s资源调度之滚动更新策略,若要实现零宕机发布,需支持健康检查

apiVersion: apps/v1
kind: Deployment
metadata:name: {APP_NAME}labels:app: {APP_NAME}
spec:selector:matchLabels:app: {APP_NAME}replicas: {REPLICAS}				# Pod副本数strategy:type: RollingUpdate				# 滚动更新策略rollingUpdate:maxSurge: 1                   # 升级过程中最多可以比原先设置的副本数多出的数量maxUnavailable: 1             # 升级过程中最多有多少个POD处于无法提供服务的状态

优雅停机

在K8s中,当我们实现滚动升级之前,务必要实现应用级别的优雅停机。否则滚动升级时,还是会影响到业务。使应用关闭线程、释放连接资源后再停止服务
业务层面

项目依赖 pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

定义访问端口、路径及权限 application.yaml

spring:application:name: <xxx>profiles:active: @profileActive@lifecycle:timeout-per-shutdown-phase: 30s     # 停机过程超时时长设置30s,超过30s,直接停机server:port: 8080shutdown: graceful                    # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机management:server:port: 50000                         # 启用独立运维端口endpoint:                             # 开启shutdown和health端点shutdown:enabled: truehealth:probes:enabled: trueendpoints:web:exposure:base-path: /actuator            # 指定上下文路径,启用相应端点include: health,shutdown

将暴露/actuator/shutdown接口,调用方式如下:

curl -X POST 127.0.0.1:50000/actuator/shutdown

运维层面

确保dockerfile模版集成curl工具,否则无法使用curl命令

FROM openjdk:8-jdk-alpine
#构建参数
ARG JAR_FILE
ARG WORK_PATH="/app"
ARG EXPOSE_PORT=8080#环境变量
ENV JAVA_OPTS=""\JAR_FILE=${JAR_FILE}#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories  \&& apk add --no-cache curl
#将maven目录的jar包拷贝到docker中,并命名为for_docker.jar
COPY target/$JAR_FILE $WORK_PATH/#设置工作目录
WORKDIR $WORK_PATH

指定于外界交互的端口

EXPOSE $EXPOSE_PORT

配置容器,使其可执行化

ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE

k8s部署模版deployment.yaml

注:经验证,java项目可省略结束回调钩子的配置

此外,若需使用回调钩子,需保证镜像中包含curl工具,且需注意应用管理端口(50000)不能暴露到公网

apiVersion: apps/v1
kind: Deployment
spec:template:spec:containers:- name: {APP_NAME}image: {IMAGE_URL}imagePullPolicy: Alwaysports:- containerPort: {APP_PORT}- containerPort: 50000lifecycle:preStop: 						# 结束回调钩子exec:command: ["curl", "-XPOST", "127.0.0.1:50000/actuator/shutdown"]

弹性伸缩

为pod设置资源限制后,创建HPA

apiVersion: apps/v1
kind: Deployment
metadata:name: {APP_NAME}labels:app: {APP_NAME}
spec:template:spec:containers:- name: {APP_NAME}image: {IMAGE_URL}imagePullPolicy: Alwaysresources:                     # 容器资源管理limits:                      # 资源限制(监控使用情况)cpu: 0.5memory: 1Girequests:                    # 最小可用资源(灵活调度)cpu: 0.15memory: 300Mi
kind: HorizontalPodAutoscaler            # 弹性伸缩控制器
apiVersion: autoscaling/v2beta2
metadata:name: {APP_NAME}
spec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: {APP_NAME}minReplicas: {REPLICAS}                # 缩放范围maxReplicas: 6metrics:- type: Resourceresource:name: cpu                        # 指定资源指标target:type: UtilizationaverageUtilization: 50

Prometheus集成
业务层面

项目依赖 pom.xml<!-- 引入Spring boot的监控机制-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

定义访问端口、路径及权限 application.yaml

management:server:port: 50000                         # 启用独立运维端口metrics:tags:application: ${spring.application.name}endpoints:web:exposure:base-path: /actuator            # 指定上下文路径,启

用相应端点
include: metrics,prometheus

将暴露/actuator/metric和/actuator/prometheus接口,访问方式如下:

http://127.0.0.1:50000/actuator/metric
http://127.0.0.1:50000/actuator/prometheus

运维层面 deployment.yaml

apiVersion: apps/v1
kind: Deployment
spec:template:metadata:annotations:prometheus:io/port: "50000"prometheus.io/path: /actuator/prometheus  # 在流水线中赋值prometheus.io/scrape: "true"              # 基于pod的服务发现

配置分离

方案:通过configmap挂载外部配置文件,并指定激活环境运行

作用:配置分离,避免敏感信息泄露;镜像复用,提高交付效率

通过文件生成configmap

通过dry-run的方式生成yaml文件

kubectl create cm -n <APP_NAME> --from-file=application-test.yaml --dry-run=1 -oyaml > configmap.yaml

更新

kubectl apply -f configmap.yaml

挂载configmap并指定激活环境

apiVersion: apps/v1
kind: Deployment
metadata:name: {APP_NAME}labels:app: {APP_NAME}
spec:template:spec:containers:- name: {APP_NAME}image: {IMAGE_URL}imagePullPolicy: Alwaysenv:- name: SPRING_PROFILES_ACTIVE   # 指定激活环境value: testvolumeMounts:                      # 挂载configmap- name: confmountPath: "/app/config"         # 与Dockerfile中工作目录一致readOnly: truevolumes:- name: confconfigMap:name: {APP_NAME}

汇总配置

业务层面

项目依赖 pom.xml<!-- 引入Spring boot的监控机制-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

定义访问端口、路径及权限 application.yaml

spring:application:name: project-sampleprofiles:active: @profileActive@lifecycle:timeout-per-shutdown-phase: 30s     # 停机过程超时时长设置30s,超过30s,直接停机server:port: 8080shutdown: graceful                    # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机management:server:port: 50000                         # 启用独立运维端口metrics:tags:application: ${spring.application.name}endpoint:                             # 开启shutdown和health端点shutdown:enabled: truehealth:probes:enabled: trueendpoints:web:exposure:base-path: /actuator            # 指定上下文路径,启用相应端点include: health,shutdown,metrics,prometheus

运维层面

确保dockerfile模版集成curl工具,否则无法使用curl命令

FROM openjdk:8-jdk-alpine
#构建参数
ARG JAR_FILE
ARG WORK_PATH="/app"
ARG EXPOSE_PORT=8080#环境变量
ENV JAVA_OPTS=""\JAR_FILE=${JAR_FILE}#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories  \&& apk add --no-cache curl
#将maven目录的jar包拷贝到docker中,并命名为for_docker.jar
COPY target/$JAR_FILE $WORK_PATH/#设置工作目录
WORKDIR $WORK_PATH# 指定于外界交互的端口
EXPOSE $EXPOSE_PORT
# 配置容器,使其可执行化
ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE

k8s部署模版deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:name: {APP_NAME}labels:app: {APP_NAME}
spec:selector:matchLabels:app: {APP_NAME}replicas: {REPLICAS}                            # Pod副本数strategy:type: RollingUpdate                           # 滚动更新策略rollingUpdate:maxSurge: 1maxUnavailable: 0template:metadata:name: {APP_NAME}labels:app: {APP_NAME}annotations:timestamp: {TIMESTAMP}prometheus.io/port: "50000"               # 不能动态赋值prometheus.io/path: /actuator/prometheusprometheus.io/scrape: "true"              # 基于pod的服务发现spec:affinity:                                   # 设置调度策略,采取多主机/多可用区部署podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- {APP_NAME}topologyKey: "kubernetes.io/hostname" # 多可用区为"topology.kubernetes.io/zone"terminationGracePeriodSeconds: 30             # 优雅终止宽限期containers:- name: {APP_NAME}image: {IMAGE_URL}imagePullPolicy: Alwaysports:- containerPort: {APP_PORT}- name: management-portcontainerPort: 50000         # 应用管理端口readinessProbe:                # 就绪探针httpGet:path: /actuator/health/readinessport: management-portinitialDelaySeconds: 30      # 延迟加载时间periodSeconds: 10            # 重试时间间隔timeoutSeconds: 1            # 超时时间设置successThreshold: 1          # 健康阈值failureThreshold: 9          # 不健康阈值livenessProbe:                 # 存活探针httpGet:path: /actuator/health/livenessport: management-portinitialDelaySeconds: 30      # 延迟加载时间periodSeconds: 10            # 重试时间间隔timeoutSeconds: 1            # 超时时间设置successThreshold: 1          # 健康阈值failureThreshold: 6          # 不健康阈值resources:                     # 容器资源管理limits:                      # 资源限制(监控使用情况)cpu: 0.5memory: 1Girequests:                    # 最小可用资源(灵活调度)cpu: 0.1memory: 200Mienv:- name: TZvalue: Asia/Shanghai
---
kind: HorizontalPodAutoscaler            # 弹性伸缩控制器
apiVersion: autoscaling/v2beta2
metadata:name: {APP_NAME}
spec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: {APP_NAME}minReplicas: {REPLICAS}                # 缩放范围maxReplicas: 6metrics:- type: Resourceresource:name: cpu                        # 指定资源指标target:type: UtilizationaverageUtilization: 50

相关文章:

K8s(健康检查+滚动更新+优雅停机+弹性伸缩+Prometheus监控+配置分离)

前言 快速配置请直接跳转至汇总配置 K8s SpringBoot实现零宕机发布&#xff1a;健康检查滚动更新优雅停机弹性伸缩Prometheus监控配置分离&#xff08;镜像复用&#xff09; 配置 健康检查 健康检查类型&#xff1a;就绪探针&#xff08;readiness&#xff09; 存活探针&am…...

Django学习记录:使用ORM操作MySQL数据库并完成数据的增删改查

Django学习记录&#xff1a;使用ORM操作MySQL数据库并完成数据的增删改查 数据库操作 MySQL数据库pymysql Django开发操作数据库更简单&#xff0c;内部提供了ORM框架。 安装第三方模块 pip install mysqlclientORM可以做的事&#xff1a; 1、创建、修改、删除数据库中的…...

React Hooks 中的 useEffect(副作用)

useEffect 是什么&#xff1f; useEffect 是一个 React Hook&#xff0c;它允许你将组件与外部系统同步 当我们在 React 中使用 useEffect 这个 Hook 时&#xff0c;实际上是在告诉 React 在特定情况下执行我们定义的副作用函数。这种副作用函数可以处理一些与组件渲染结果无关…...

[CKA]考试之持久化存储卷PersistentVolume

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 创建一个pv&#xff0c;名字为app-config&#xff0c;大小为2Gi&#xff0c;…...

基于LLM的SQL应用程序开发实战(一)

基于LLM的SQL应用程序开发实战(一) 16.1 SQL on LLMs应用程序初始化 本节主要从案例代码的角度切入,探索ChatGPT以及大模型,尤其是从生产环境的视角,来思考具体的最佳实践。本节主要跟大家谈的是,在LangChain这样一个框架下,我们使用GPT-3.5或者GPT-4大模型,同时使用第…...

如何批量实现多行合并后居中

思路&#xff1a; 1.先填充数据 2.数据分类统计制作格式 3.格式刷刷制作出的格式 1.填充数据 思路&#xff1a;选中&#xff0c;F5定位空值&#xff0c;&#xff0c;⬆&#xff08;键盘上的上下左右哪里的上键&#xff09;&#xff0c;按住Ctrl然后按Enter。 2.数据分类统计…...

【深度学习_TensorFlow】手写数字识别

写在前面 到这里为止&#xff0c;我们已经学习完张量的常用操作方法&#xff0c;已具备实现大部分神经网络技术的基础储备了。这一章节我们将开启神经网络的学习&#xff0c;然而并不需要像学习前面那样了解大量的张量操作&#xff0c;而是将重点转向理解概念知识&#xff0c;…...

antv/l7地图,鼠标滚动,页面正常滑动-- 我们忽略的deltaY

背景 在官网项目中&#xff0c;需要使用一个地图&#xff0c;展示产品的分布区域及数量。希望的交互是&#xff0c;鼠标放上标点&#xff0c;tooltip展示地点和数量等信息。鼠标滚动&#xff0c;则页面随着滚动。但是鼠标事件是被地图代理了的&#xff0c;鼠标滚动意味着地图的…...

再续AM335x经典,米尔TI AM62x核心板上市,赋能新一代HMI

近十年来&#xff0c;AM335x芯片作为TI经典工业MPU产品&#xff0c;在工业处理器市场占据主流地位&#xff0c;其凭借GPMC高速并口、PRU协处理器等个性化硬件资源&#xff0c;在工业控制、能源电力、轨道交通、智慧医疗等领域广受用户欢迎。随着信息技术的快速发展&#xff0c;…...

springboot和Django哪一个做web服务器框架更好

目录 一、两者特点 二、各自优势 一、两者特点 编程语言&#xff1a; Spring Boot&#xff1a;使用 Java 编程语言。Django&#xff1a;使用 Python 编程语言。 生态系统和社区支持&#xff1a; Spring Boot&#xff1a;具有庞大的 Java 生态系统和强大的社区支持。适用于大型…...

C#核心知识回顾——21.归并排序

理解递归逻辑 一开始不会执行sort函数的 要先找到最小容量数组时 才会回头递归调用Sort进行排序 基本原理 归并 递归 合并 数组分左右 左右元素相比较 一侧用完放对面 不停放入新数组 递归不停分 分…...

基于netty的rpc远程调用

QPRC &#x1f680;&#x1f680;&#x1f680;这是一个手写RPC项目&#xff0c;用于实现远程过程调用&#xff08;RPC&#xff09;通信&#x1f680;&#x1f680;&#x1f680; 欢迎star串门&#xff1a;https://github.com/red-velet/ &#x1f680;Q-PRC 一、功能特性 …...

RabbitMQ输出日志配置

参考地址rabbitmq启用日志功能记录消息队列收发情况_rabbitmq开启日志_普通网友的博客-CSDN博客 启用日志插件命令 # 设置用户权限 rabbitmqctl set_user_tags mqtt-user administrator rabbitmqctl set_permissions -p / mqtt-user ".*" ".*" ".*&…...

解决一个Sqoop抽数慢的问题,yarn的ATSv2嵌入式HBASE崩溃引起

新搭建的一个Hadoop环境&#xff0c;用Sqoop批量抽数的时候发现特别慢&#xff0c;我们正常情况下是一个表一分钟左右&#xff0c;批量抽十几个表&#xff0c;也就是10分钟的样子&#xff0c;结果发现用了2个小时&#xff1a; 查看yarn日志 发现有如下情况&#xff1a; 主要有两…...

为Android构建现代应用——应用导航设计

在前一章节的实现中&#xff0c;Skeleton: Main structure&#xff0c;我们留下了几个 Jetpack 架构组件&#xff0c;这些组件将在本章中使用&#xff0c;例如 Composables、ViewModels、Navigation 和 Hilt。此外&#xff0c;我们还通过 Scaffold 集成了 TopAppBar 和 BottomA…...

聊聊 Docker 和 Dockerfile

目录 一、前言 二、了解Dockerfile 三、Dockerfile 指令 四、多阶段构建 五、Dockerfile 高级用法 六、小结 一、前言 对于开发人员来说&#xff0c;会Docker而不知道Dockerfile等于不会Docker&#xff0c;上一篇文章带大家学习了Docker的基本使用方法&#xff1a;《一文…...

element表格+表单+表单验证结合u

一、结果展示 1、图片 2、描述 table中放form表单&#xff0c;放输入框或下拉框或多选框等&#xff1b; 点击添加按钮&#xff0c;首先验证表单&#xff0c;如果存在没填的就验证提醒&#xff0c;都填了就向下添加一行表单表格&#xff1b; 点击当前行删除按钮&#xff0c;…...

数据库:MYSQL参数max_allowed_packet 介绍

1、参数作用 max_allowed_packet参数是指mysql服务器端和客户端在一次传送数据包的过程当中最大允许的数据包大小。如果超过了设置的最大长度,则会数据库保持数据失败。 2、问题场景 ● 有时候业务的需要,可能会存在某些字段数据长度非常大(比如富文本编辑器里面的内容),…...

基于DiscordMidjourney API接口实现文生图

https://discord.com/api/v9/interactions 请求头&#xff1a; authorization:取自 浏览器中discord 文生图请求头中的 authorization 的值 Content-Type:application/json 请求体&#xff1a; {“type”:2,“application_id”:“93692956130267xxxx”,“guild_id”:“1135900…...

springboot+vue农产品特产商城销售平台_50kf2 多商家

随着我国经济的高速发展与人们生活水平的日益提高&#xff0c;人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下&#xff0c;人们更趋向于足不出户解决生活上的问题&#xff0c;南阳特产销售平台展现了其蓬勃生命力和广阔的前景。与此同时&#xff0c;为解决…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...