引子-从传统的安装包到云原生时代的应用
过去,在传统的通过shell命令行部署的时代,开发者们习惯于按照面向过程的方式一步步通过代码编译构建打包,然后把安装包远程部署到环境上,安装包就是安装包,环境就是环境,部署脚本就是部署脚本。
随着k8s云原生和DevOps的普及,一种新的实践形式"应用" 脱颖而出,它的具体表现是通过一组声明式资源配置(Deployment、Service、ConfigMap 等)定义的容器化服务,可伸缩,可观测,通过GitOps直接从"代码"到"可运行的服务"。
在持续交付中,"应用"是最终要交付给用户的功能性实体,可以是单体应用(Monolithic Application)、云原生应用(Cloud-Native Application)、移动应用,这些仅仅是技术架构层面的解释,那么对于研发生命周期而言,应用又会带来什么不同的价值呢?
本篇文章将带大家从"研发生命周期"和"DevOps平台建设"的角度,诠释"应用新的含义"。
以"应用为中心"-建立"管理过程"和"研发过程"的纽带
如下图,这是一个很典型的产品版本需求迭代,到部署发布的简单示意图。

接着下面的图,当代码提交入库,触发Pipeline,最终形成一个可部署运行的应用服务。

这里我们思考几个问题
-
- 从"产品(需求)" 到"服务应用"之间是什么关系?前者是业务,后者是技术,从业务到技术实现如何表达?
-
- 将"一个市场需求",快速的实现并且上线,需要多少个步骤?需要几个代码库的变更?涉及哪几个安装包/镜像?又有几个流水线需要编排?
-
- 在复杂且冗长的工具链编排中,如何精准的定位追溯每一次变更,快速的试错和回滚?
看到这里,可能你会习以为常,我们平时就是这样做的,有什么问题?
从"面向过程"到"面向对象",用"声明方式"描述"产品骨架"
如下图,和上面的图进行对比,是不是有点"面向对象"的感觉?

对照上面的图,再看看下面的术语解释,仔细琢磨下
-
- 产品:提供用户价值(即组织发布活动)的独立单元,是由独立的产、研、测共同维护的功能和服务的集合。
-
- 应用:对应一个可独立运行、发布的线上服务叫做应用,是软件交付和运维的最小单元。应用对应某一个代码库,对应部署发布的应用。
-
- 版本:版本是产品的基础属性,产品每一次发布(可能涉及多个开发团队多个应用),产生该产品(系列需求)的一个新版本(结果)。新版本制作过程包含产研全生命周期。
云原生应用
基于上面的图,AI帮忙编写了对应的配置声明,帮助大家理解(片段有点长, 可以快速跳过)
yaml
# ================================================
# 应用部署模板 - Kubernetes 云原生服务编排示例
# 对应架构图左侧「代码库」「制品/镜像」「配置信息」与右侧「部署环境」
# ================================================
---
# 部署环境定义(对应架构图右侧「dev/uat/prod」)
# 使用命名空间隔离不同环境(此处以prod为例)
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
env: production
---
# ------------------------------------------------
# 核心部署配置(对应架构图左侧「制品/镜像」+「配置信息」)
# ------------------------------------------------
# 配置信息(ConfigMap对应架构图左侧「配置信息」模块)
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: prod # 绑定prod环境命名空间
data:
app.properties: |
# 应用运行时配置
db.host=mysql.prod.svc.cluster.local
cache.enabled=true
log.level=INFO
---
# 敏感信息配置(Secret属于特殊类型的配置信息)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: prod
type: Opaque
data:
username: YWRtaW4= # admin base64编码
password: cGFzc3dvcmQxMjM= # password123 base64编码
---
# ------------------------------------------------
# 服务编排核心定义(对应架构图下方「服务编排(云原生)」模块)
# ------------------------------------------------
# 部署控制器(Deployment实现滚动更新策略)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment # 部署版本标识
namespace: prod
labels:
app: myapp
track: stable # 部署策略标识
spec:
replicas: 3 # 初始副本数
strategy:
type: RollingUpdate # 部署策略-滚动更新
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
env: prod
spec:
containers:
- name: app-container
image: registry.example.com/myapp:v1.2.3 # 制品/镜像(来自CI/CD流水线构建)
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db.host
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe: # 健康检查(可观测性)
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
---
# 服务暴露(Service实现服务发现)
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: prod
spec:
type: ClusterIP # 生产环境建议使用LoadBalancer
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
---
# ------------------------------------------------
# 弹性伸缩策略(对应架构图「部署策略」模块)
# ------------------------------------------------
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
# ================================================
# 部署执行说明:
# 1. 代码库变更触发CI/CD流水线构建镜像(更新image版本)
# 2. 通过kubectl apply -f 应用部署模板文件.yaml
# 3. 监控部署状态:kubectl -n prod get pods,svc,hpa
# ================================================
传统主机部署应用
上面说的是k8s应用,那么传统安装包主机部署呢?怎么通过应用表达?
通过 YAML 定义主机环境、应用包版本和全局配置(对应图中「制品/镜像」「配置信息」模块)
yaml
# File: infrastructure.yaml
# 定义主机环境、应用包版本等基础设施信息
environments:
- name: dev
hosts:
- ip: 192.168.1.101
user: dev-user
deploy_dir: /opt/apps/dev
app_version: 1.0.0-SNAPSHOT
- name: prod
hosts:
- ip: 192.168.1.200
user: prod-admin
deploy_dir: /opt/apps/prod
app_version: 1.0.0-RELEASE
# 全局配置模板(与环境解耦)
global_config:
app_name: "my-spring-boot-app"
java_opts: "-Xmx512m -Xms256m"
health_check_url: "/actuator/health"
每个环境(dev/prod)独立维护敏感配置(对应图中「配置信息」与「部署环境」分离)
ini
# File: config/prod.env
# 生产环境数据库配置(敏感信息单独管理)
DB_HOST=mysql.prod.internal
DB_PORT=3306
DB_USER=admin
DB_PASSWORD=encrypted_password_here # 建议使用加密工具(如Ansible Vault)
# File: config/dev.env
# 开发环境数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=dev
DB_PASSWORD=dev123
通过 Shell 脚本实现自动化部署(对应图中「部署脚本」模块)
bash
#!/bin/bash
# File: deploy.sh
# 参数化部署:指定环境(dev/prod)和应用包路径
ENV=$1
APP_PACKAGE=$2
# 加载基础设施定义
INFRA_FILE="infrastructure.yaml"
APP_NAME=$(yq eval '.global_config.app_name' $INFRA_FILE)
DEPLOY_DIR=$(yq eval ".environments[] | select(.name == \"$ENV\").hosts[0].deploy_dir" $INFRA_FILE)
REMOTE_USER=$(yq eval ".environments[] | select(.name == \"$ENV\").hosts[0].user" $INFRA_FILE)
REMOTE_IP=$(yq eval ".environments[] | select(.name == \"$ENV\").hosts[0].ip" $INFRA_FILE)
# 加载环境配置
CONFIG_FILE="config/$ENV.env"
source $CONFIG_FILE
# 生成动态应用配置(模板化)
cat > application.properties <<EOF
# Auto-generated configuration for $ENV
spring.datasource.url=jdbc:mysql://${DB_HOST}:${DB_PORT}/${APP_NAME}_${ENV}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
server.port=8080
EOF
# 执行部署动作
echo "Deploying $APP_NAME ($ENV) to $REMOTE_IP..."
ssh $REMOTE_USER@$REMOTE_IP "mkdir -p $DEPLOY_DIR && systemctl stop $APP_NAME"
scp $APP_PACKAGE $REMOTE_USER@$REMOTE_IP:$DEPLOY_DIR/
scp application.properties $REMOTE_USER@$REMOTE_IP:$DEPLOY_DIR/config/
# 启动服务(假设已配置systemd服务单元)
ssh $REMOTE_USER@$REMOTE_IP "\
chmod +x $DEPLOY_DIR/$(basename $APP_PACKAGE) && \
systemctl daemon-reload && \
systemctl start $APP_NAME && \
systemctl status $APP_NAME"
通过模板定义应用服务管理(对应「基础设施即代码」)
ini
# File: templates/my-spring-boot-app.service.j2
# Systemd 服务单元模板(由部署脚本动态生成)
[Unit]
Description={{ global_config.app_name }} ({{ env }})
After=network.target
[Service]
User={{ user }}
WorkingDirectory={{ deploy_dir }}
ExecStart=/usr/bin/java {{ global_config.java_opts }} -jar {{ deploy_dir }}/{{ app_package }}
EnvironmentFile={{ deploy_dir }}/config/application.properties
Restart=always
[Install]
WantedBy=multi-user.target
根据环境参数执行相同脚本部署不同的包
bash
# 部署到生产环境
./deploy.sh prod my-app-1.0.0-RELEASE.jar
# 部署到开发环境
./deploy.sh dev my-app-1.0.0-SNAPSHOT.jar
从"应用为中心"看DevOps平台演进的趋势
通过上面对于"应用"在研发生命周期的桥梁作用,再到具体的技术实践,作为DevOps平台或者体系的搭建者,我总结了以下几点趋势:
-
- 越来越多的平台开始通过"应用"来进行"构建编排流程的封装",从"面向过程"到"面向对象",通过更简单的声明,屏蔽底层工具和步骤的复杂性,帮助产品更快交付。

-
- DevOps平台的建设从开始的"工具统一"到代码"工程化统一"(应用),最终会向到"研发流程标准化"演进,将更多的研发规范,最佳实践等标准化要求变成"企业的研发模版"。开发者只用专注于业务代码本身,规范和要求在潜移默化中被遵守。
-
- 对于持续交付而言,逐步的从"面向过程"(1.0-脚本编排) 到"面向对象"(2.0-应用配置声明),再到 "面向业务流程" (3.0-研发标准流程模版化),这个是必然趋势。特别是在AI时代,更快的代码交付,对于工程化,基础设施,标准规范的快速切实执行提出了更高的要求。
