在当今的快节奏开发环境中,确保前端服务的高效和可靠更新至关重要。本文将介绍如何通过AWS服务(如ECS和S3)以及Jenkins进行参数化构建,实现前端服务的自动化部署和更新控制。我们将深入了解使用Python脚本管理部署流程,以及如何在Jenkins中配置参数化构建,使得服务的部署更加灵活和可配置。

部署脚本

Python脚本管理部署流程

首先,让我们来看一下负责编排部署流程的Python脚本。该脚本使用Boto3库与AWS服务进行交互,并利用Requests库发起HTTP请求。以下是关键功能的概述:

#!/usr/bin/python3
import os
import requests
import boto3
import json


def get_secret(secret_name, region_name):
    """
    Retrieves the specified secret from AWS Secrets Manager.

    Args:
        secret_name (str): The name of the secret.
        region_name (str): The AWS region where the secret is stored.

    Returns:
        dict: The decoded JSON representation of the secret.
    """
    session = boto3.session.Session()
    client = session.client(service_name='secretsmanager', region_name=region_name)
    get_secret_value_response = client.get_secret_value(SecretId=secret_name)
    return json.loads(get_secret_value_response['SecretString'])


def send_dingding(service_name, token):
    """
    Sends a message to DingTalk notifying the successful restart of a service.

    Args:
        service_name (str): Name of the service being restarted.
        token (str): DingTalk access token.
    """
    headers = {'Content-Type': 'application/json'}
    message = f"<font face='黑体' color='#00EC00'>Restart {service_name} Success</font>\n"
    data = {"msgtype": "markdown", "markdown": {"title": f"Restart {service_name} Success", "text": message}}
    response = requests.post(f"https://oapi.dingtalk.com/robot/send?access_token={token}", headers=headers, json=data)
    print(response.text)


def update_ecs_service(service_name, cluster_name='frontend-pro'):
    """
    Updates the ECS service with a new deployment.

    Args:
        service_name (str): Name of the ECS service.
        cluster_name (str): Name of the ECS cluster.
    """
    boto3.client('ecs').update_service(cluster=cluster_name, service=service_name, taskDefinition=service_name,
                                       forceNewDeployment=True)
    print("****************************更新容器************************************")


def upload_to_s3(service_name):
    """
    Uploads code to S3 bucket.

    Args:
        service_name (str): Name of the service.
    """
    s3_client = boto3.client('s3')
    print("****************************上传代码************************************")
    s3_client.sync(f"s3://deploy-github-fe/{service_name}/", f"s3://static/prod/{service_name}/",
                   acl='public-read')


def clear_url_cache():
    """
    Clears the URL cache by sending an HTTP POST request.
    """
    response = requests.post("https://apph5-api.my.com/h5/v1/commom/cache/reset")
    print(response.text)


def deploy_services(service_names):
    """
    Deploys services based on their names.

    Args:
        service_names (list): List of service names to be deployed.
    """
    for service_name in service_names:
        if service_name == "pro-trade-fe":
            upload_to_s3(service_name)
            update_ecs_service(service_name)
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])
        elif service_name in ["pro-auth-fe", "prod-h5-game-fe"]:
            upload_to_s3(service_name)
            clear_url_cache()
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])
        elif service_name == "prod-life-web-fe":
            update_ecs_service(service_name)
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])
        elif service_name in ["prod-app-h5-fe", "prod-app-hd-fe"]:
            upload_to_s3(service_name)
            update_ecs_service(service_name)
            clear_url_cache()
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])
        elif service_name == "prod-minger-os-web-fe":
            update_ecs_service(service_name)
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])
        else:
            upload_to_s3(service_name)
            update_ecs_service(service_name)
            clear_url_cache()
            send_dingding(service_name, os.environ['DINGDING_TOKEN'])


if __name__ == "__main__":
    # Get DingDing token from AWS Secrets Manager
    secrets = get_secret('base', 'us-east-1')
    token = secrets.get('BUILD1_TOKEN', '')  # Retrieve the token, default to empty string if not found

    # Get service names from environment variable
    service_names = os.environ['SERVICE_NAMES'].split(',')

    # Execute deployment
    deploy_services(service_names)

关键功能解析

  • get_secret:从AWS Secrets Manager检索指定的秘密。
  • send_dingding:向DingTalk发送有关服务重新启动的消息。
  • update_ecs_service:使用新的部署更新ECS服务。
  • upload_to_s3:将代码上传到S3桶。
  • clear_url_cache:通过发送HTTP POST请求清除URL缓存。
  • deploy_services:部署服务的主要函数。

Shell脚本管理部署流程

#!/bin/bash

# Function to send DingTalk notification
send_dingding() {
    DINGDING_TOKEN=YOU_DINGDING_TOKEN
    cat <<EOF >send_info.sh
curl "https://oapi.dingtalk.com/robot/send?access_token=$DINGDING_TOKEN" \
     -H 'Content-Type: application/json' \
     -d '{"msgtype": "markdown","markdown": {"title":"Restart $SERVICE_NAME Success","text": "<font face='黑体' color='#00EC00'>Restart $SERVICE_NAME Success</font>\n"}}'
EOF

    bash send_info.sh
}

# Function to update Docker containers
update_docker() {
    echo "****************************更新容器************************************"
    /usr/local/bin/aws ecs update-service --cluster frontend-pro --region us-east-1 \
                                           --force-new-deployment --service "${SERVICE_NAME}" \
                                           --task-definition "${SERVICE_NAME}"
}

# Function to upload code to S3
upload_to_s3() {
    echo "****************************上传代码************************************"
    /usr/local/bin/aws s3 sync "s3://deploy-github-fe/${SERVICE_NAME}/" \
                                "s3://static/prod/${SERVICE_NAME}/" --acl public-read
}

# Function to clear URL cache
clear_url_cache() {
    curl "https://apph5-api.my.com/h5/v1/commom/cache/reset" -X POST
}

# Function to deploy services
deploy() {
    echo "${SERVICE_NAME}" > /tmp/SERVICE_NAME
    sed -i "s|,| |g" /tmp/SERVICE_NAME
    sed -i 's|"| |g' /tmp/SERVICE_NAME

    for SERVICE_NAME in $(cat /tmp/SERVICE_NAME); do
        # 根据不同的服务名称执行不同的操作
        if [[ "${SERVICE_NAME}" == "pro-trade-fe" ]]; then
            upload_to_s3
            update_docker
            send_dingding
        elif [[ "${SERVICE_NAME}" == "pro-auth-fe" || "${SERVICE_NAME}" == "prod-h5-game-fe" ]]; then
            upload_to_s3
            clear_url_cache
            send_dingding
        elif [[ "${SERVICE_NAME}" == "prod-life-web-fe" ]]; then
            update_docker
            send_dingding
        elif [[ "${SERVICE_NAME}" == "prod-app-h5-fe" || "${SERVICE_NAME}" == "prod-app-hd-fe" ]]; then
            upload_to_s3
            update_docker
            clear_url_cache
            send_dingding
        elif [[ "${SERVICE_NAME}" == "prod-minger-os-web-fe" ]]; then
            update_docker
            send_dingding
        else
            upload_to_s3
            update_docker
            clear_url_cache
            send_dingding
        fi
    done
}

# Execute deployment
deploy

上述脚本是一个用于部署服务的 Bash 脚本,通过函数化的方式实现了不同阶段的操作。以下是对脚本中各部分的注解:

  • send_dingding(): 发送钉钉通知的函数,使用 DingTalk 机器人实现。
  • update_docker(): 更新 Docker 容器的函数,使用 AWS ECS 提供的命令行工具进行服务更新。
  • upload_to_s3(): 上传代码到 S3 的函数,使用 AWS S3 提供的命令行工具进行文件同步。
  • clear_url_cache(): 清除 URL 缓存的函数,通过发送 HTTP POST 请求实现。
  • deploy(): 部署函数,根据不同服务类型调用相应的函数。

Jenkins中的参数化构建

为了使部署流程更加灵活和可配置,我们将该脚本集成到Jenkins中,并使用Extended Choice Parameter插件进行参数化构建。这使用户能够在Jenkins构建期间选择要部署的服务。

Jenkins配置

  • Extended Choice Parameter:
  • Name: SERVICE_NAMES
  • Parameter Type: Check Boxes
  • Number of Visible Items: 10
  • Delimiter:,
  • Quote Value: prod-app-hd-fe, prod-app-h5-fe, prod-govee-life-web-fe, prod-community-web-fe, prod-app-platform-fe, pro-trade-fe, pro-auth-fe, prod-h5-game-fe, pro-app-h5-fe, prod-promotion-app-h5-fe, prod-desktop-web-fe, prod-app-mall-fe, prod-minger-os-web-fe

结论

通过自动化部署流水线,我们不仅节省了时间,还减少了人为错误的风险。通过Python脚本和Jenkins的集成,只需几次点击即可轻松部署前端服务到AWS。根据您的特定用例进行定制,享受简化的部署流程。

在您的博文中,您可以进一步优化、整理和详细说明脚本的生产控制、开发最佳实践等方面的内容,以使读者更好地理解和应用这一自动化流程。祝愉快编写博文!