D AWS oc AWS SDK 示例
本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
AWS Batch 使用示例 AWS CLI 使用 Bash 脚本
以下代码示例向您展示了如何使用 with Bash 脚本来执行操作和实现常见场景。 AWS Command Line Interface AWS Batch
场景是向您演示如何通过在一个服务中调用多个函数或与其他 AWS 服务结合来完成特定任务的代码示例。
每个示例都包含一个指向完整源代码的链接,您可以从中找到有关如何在上下文中设置和运行代码的说明。
主题
场景
以下代码示例展示了如何:
创建 IAM 执行角色
创建计算环境
创建作业队列
创建任务定义
清理资源
- AWS CLI 使用 Bash 脚本
-
注意
还有更多 GitHub。在 Sample developer tutorials
存储库中查找完整示例,了解如何进行设置和运行。 #!/bin/bash # AWS Batch Fargate Getting Started Script - Security Hardened Version # This script demonstrates creating AWS Batch resources with Fargate orchestration # set -euo pipefail # Exit on any error, undefined variables, and pipe failures # Configuration SCRIPT_NAME="batch-fargate-tutorial" LOG_FILE="${SCRIPT_NAME}-$(date +%Y%m%d-%H%M%S).log" RANDOM_SUFFIX=$(openssl rand -hex 6) COMPUTE_ENV_NAME="batch-fargate-compute-${RANDOM_SUFFIX}" JOB_QUEUE_NAME="batch-fargate-queue-${RANDOM_SUFFIX}" JOB_DEF_NAME="batch-fargate-jobdef-${RANDOM_SUFFIX}" JOB_NAME="batch-hello-world-${RANDOM_SUFFIX}" ROLE_NAME="BatchEcsTaskExecutionRole-${RANDOM_SUFFIX}" TRUST_POLICY_FILE="batch-trust-policy-${RANDOM_SUFFIX}.json" # Security: Set restrictive umask umask 0077 # Array to track created resources for cleanup CREATED_RESOURCES=() # Logging function with sanitization log() { local message="${1//[$'\t\r\n']/}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${message}" | tee -a "${LOG_FILE}" } # Error handling function handle_error() { log "ERROR: Script failed at line $1" log "Attempting to clean up resources created so far..." cleanup_resources exit 1 } # Set up error handling trap 'handle_error ${LINENO}' ERR # Validate AWS credentials validate_aws_credentials() { if ! aws sts get-caller-identity &>/dev/null; then log "ERROR: AWS credentials are not configured or invalid" exit 1 fi } # Function to wait for resource to be ready wait_for_compute_env() { local env_name="${1}" local max_attempts=60 local attempt=0 log "Waiting for compute environment ${env_name} to be VALID..." while [ ${attempt} -lt ${max_attempts} ]; do local status status=$(aws batch describe-compute-environments \ --compute-environments "${env_name}" \ --query 'computeEnvironments[0].status' \ --output text 2>/dev/null || echo "NOT_FOUND") if [ "${status}" = "VALID" ]; then log "Compute environment ${env_name} is ready" return 0 elif [ "${status}" = "INVALID" ] || [ "${status}" = "NOT_FOUND" ]; then log "ERROR: Compute environment ${env_name} failed to create properly" return 1 fi log "Compute environment status: ${status}. Waiting 10 seconds..." sleep 10 ((attempt++)) done log "ERROR: Timeout waiting for compute environment ${env_name}" return 1 } # Function to wait for job queue to be ready wait_for_job_queue() { local queue_name="${1}" local max_attempts=60 local attempt=0 log "Waiting for job queue ${queue_name} to be VALID..." while [ ${attempt} -lt ${max_attempts} ]; do local state state=$(aws batch describe-job-queues \ --job-queues "${queue_name}" \ --query 'jobQueues[0].state' \ --output text 2>/dev/null || echo "NOT_FOUND") if [ "${state}" = "ENABLED" ]; then log "Job queue ${queue_name} is ready" return 0 elif [ "${state}" = "DISABLED" ] || [ "${state}" = "NOT_FOUND" ]; then log "ERROR: Job queue ${queue_name} failed to create properly" return 1 fi log "Job queue state: ${state}. Waiting 10 seconds..." sleep 10 ((attempt++)) done log "ERROR: Timeout waiting for job queue ${queue_name}" return 1 } # Function to wait for job completion wait_for_job() { local job_id="${1}" local max_attempts=120 local attempt=0 log "Waiting for job ${job_id} to complete..." while [ ${attempt} -lt ${max_attempts} ]; do local status status=$(aws batch describe-jobs \ --jobs "${job_id}" \ --query 'jobs[0].status' \ --output text 2>/dev/null || echo "NOT_FOUND") if [ "${status}" = "SUCCEEDED" ]; then log "Job ${job_id} completed successfully" return 0 elif [ "${status}" = "FAILED" ]; then log "ERROR: Job ${job_id} failed" return 1 fi log "Job status: ${status}. Waiting 30 seconds..." sleep 30 ((attempt++)) done log "ERROR: Timeout waiting for job ${job_id}" return 1 } # Function to wait for resource state before deletion wait_for_resource_state() { local resource_type="${1}" local resource_name="${2}" local expected_state="${3}" local max_attempts=30 local attempt=0 log "Waiting for ${resource_type} ${resource_name} to reach state: ${expected_state}" while [ ${attempt} -lt ${max_attempts} ]; do local current_state="" case "${resource_type}" in "JOB_QUEUE") current_state=$(aws batch describe-job-queues \ --job-queues "${resource_name}" \ --query 'jobQueues[0].state' \ --output text 2>/dev/null || echo "NOT_FOUND") ;; "COMPUTE_ENV") current_state=$(aws batch describe-compute-environments \ --compute-environments "${resource_name}" \ --query 'computeEnvironments[0].status' \ --output text 2>/dev/null || echo "NOT_FOUND") ;; *) log "WARNING: Unknown resource type: ${resource_type}" return 1 ;; esac if [ "${current_state}" = "${expected_state}" ]; then log "${resource_type} ${resource_name} is now in state: ${expected_state}" return 0 fi log "${resource_type} ${resource_name} state: ${current_state} (waiting for ${expected_state})" sleep 10 ((attempt++)) done log "WARNING: ${resource_type} ${resource_name} did not reach expected state after ${max_attempts} attempts" return 1 } # Cleanup function cleanup_resources() { log "Starting cleanup of created resources..." # Clean up in reverse order of creation for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do local resource="${CREATED_RESOURCES[i]}" local resource_type local resource_name resource_type=$(echo "${resource}" | cut -d: -f1) resource_name=$(echo "${resource}" | cut -d: -f2-) log "Cleaning up ${resource_type}: ${resource_name}" case "${resource_type}" in "JOB_QUEUE") aws batch update-job-queue --job-queue "${resource_name}" --state DISABLED 2>/dev/null || true wait_for_resource_state "JOB_QUEUE" "${resource_name}" "DISABLED" || true aws batch delete-job-queue --job-queue "${resource_name}" 2>/dev/null || true ;; "COMPUTE_ENV") aws batch update-compute-environment --compute-environment "${resource_name}" --state DISABLED 2>/dev/null || true wait_for_resource_state "COMPUTE_ENV" "${resource_name}" "DISABLED" || true aws batch delete-compute-environment --compute-environment "${resource_name}" 2>/dev/null || true ;; "IAM_ROLE") aws iam detach-role-policy --role-name "${resource_name}" --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 2>/dev/null || true aws iam delete-role --role-name "${resource_name}" 2>/dev/null || true ;; "FILE") rm -f "${resource_name}" 2>/dev/null || true ;; *) log "WARNING: Unknown resource type for cleanup: ${resource_type}" ;; esac done log "Cleanup completed" } # Validate input parameters validate_inputs() { if [ -z "${ACCOUNT_ID:-}" ]; then log "ERROR: ACCOUNT_ID is not set" return 1 fi if [ -z "${DEFAULT_VPC:-}" ]; then log "ERROR: DEFAULT_VPC is not set" return 1 fi if [ -z "${SUBNETS:-}" ]; then log "ERROR: SUBNETS is not set" return 1 fi } # Validate container image format validate_container_image() { local image="${1}" # Check if image contains any shell metacharacters that could be dangerous if [[ "${image}" =~ [';$`|&<>()[]{}\\'] ]]; then log "ERROR: Container image contains potentially dangerous characters: ${image}" return 1 fi # Basic ECR/Docker image format validation if ! [[ "${image}" =~ ^[a-zA-Z0-9._/:-]+$ ]]; then log "ERROR: Container image format is invalid: ${image}" return 1 fi return 0 } # Main script execution main() { log "Starting AWS Batch Fargate tutorial script - Security Hardened Version" log "Log file: ${LOG_FILE}" # Validate AWS credentials validate_aws_credentials # Get AWS account ID log "Getting AWS account ID..." local ACCOUNT_ID ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) if [ -z "${ACCOUNT_ID}" ] || [ "${ACCOUNT_ID}" = "None" ]; then log "ERROR: Could not retrieve AWS account ID" exit 1 fi log "Account ID: ${ACCOUNT_ID}" # Get default VPC and subnets log "Getting default VPC and subnets..." local DEFAULT_VPC DEFAULT_VPC=$(aws ec2 describe-vpcs \ --filters "Name=is-default,Values=true" \ --query 'Vpcs[0].VpcId' \ --output text) if [ "${DEFAULT_VPC}" = "None" ] || [ "${DEFAULT_VPC}" = "null" ] || [ -z "${DEFAULT_VPC}" ]; then log "ERROR: No default VPC found. Please create a VPC first." exit 1 fi log "Default VPC: ${DEFAULT_VPC}" # Get subnets in the default VPC local SUBNETS SUBNETS=$(aws ec2 describe-subnets \ --filters "Name=vpc-id,Values=${DEFAULT_VPC}" \ --query 'Subnets[*].SubnetId' \ --output text) if [ -z "${SUBNETS}" ]; then log "ERROR: No subnets found in default VPC" exit 1 fi # Convert tab/space-separated subnets to JSON array format local SUBNET_ARRAY SUBNET_ARRAY=$(echo "${SUBNETS}" | tr '\t ' '\n' | sed 's/^/"/;s/$/"/' | paste -sd ',' -) log "Subnets: ${SUBNETS}" log "Subnet array: [${SUBNET_ARRAY}]" # Get default security group for the VPC local DEFAULT_SG DEFAULT_SG=$(aws ec2 describe-security-groups \ --filters "Name=vpc-id,Values=${DEFAULT_VPC}" "Name=group-name,Values=default" \ --query 'SecurityGroups[0].GroupId' \ --output text) if [ "${DEFAULT_SG}" = "None" ] || [ "${DEFAULT_SG}" = "null" ] || [ -z "${DEFAULT_SG}" ]; then log "ERROR: No default security group found in VPC" exit 1 fi log "Default security group: ${DEFAULT_SG}" # Step 1: Create IAM execution role log "Step 1: Creating IAM execution role..." # Create trust policy document with proper escaping cat > "${TRUST_POLICY_FILE}" << 'EOFPOLICY' { "Version":"2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOFPOLICY CREATED_RESOURCES+=("FILE:${TRUST_POLICY_FILE}") # Validate trust policy file before using it if ! jq empty "${TRUST_POLICY_FILE}" 2>/dev/null; then log "ERROR: Trust policy file is not valid JSON" exit 1 fi # Create the role aws iam create-role \ --role-name "${ROLE_NAME}" \ --assume-role-policy-document "file://${TRUST_POLICY_FILE}" aws iam tag-role --role-name "${ROLE_NAME}" --tags Key=project,Value=doc-smith Key=tutorial,Value=getting-started-batch-fargate CREATED_RESOURCES+=("IAM_ROLE:${ROLE_NAME}") # Attach policy aws iam attach-role-policy \ --role-name "${ROLE_NAME}" \ --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" log "IAM role created: ${ROLE_NAME}" # Wait for IAM role propagation log "Waiting for IAM role propagation (15 seconds)..." sleep 15 # Step 2: Create compute environment log "Step 2: Creating Fargate compute environment..." aws batch create-compute-environment \ --compute-environment-name "${COMPUTE_ENV_NAME}" \ --type MANAGED \ --state ENABLED \ --tags project=doc-smith,tutorial=getting-started-batch-fargate \ --compute-resources "{ \"type\": \"FARGATE\", \"maxvCpus\": 256, \"subnets\": [${SUBNET_ARRAY}], \"securityGroupIds\": [\"${DEFAULT_SG}\"] }" CREATED_RESOURCES+=("COMPUTE_ENV:${COMPUTE_ENV_NAME}") # Wait for compute environment to be ready if ! wait_for_compute_env "${COMPUTE_ENV_NAME}"; then log "ERROR: Compute environment failed to reach VALID state" exit 1 fi # Step 3: Create job queue log "Step 3: Creating job queue..." aws batch create-job-queue \ --job-queue-name "${JOB_QUEUE_NAME}" \ --state ENABLED \ --priority 900 \ --tags project=doc-smith,tutorial=getting-started-batch-fargate \ --compute-environment-order "order=1,computeEnvironment=${COMPUTE_ENV_NAME}" CREATED_RESOURCES+=("JOB_QUEUE:${JOB_QUEUE_NAME}") # Wait for job queue to be ready if ! wait_for_job_queue "${JOB_QUEUE_NAME}"; then log "ERROR: Job queue failed to reach ENABLED state" exit 1 fi # Step 4: Create job definition log "Step 4: Creating job definition..." local CONTAINER_IMAGE="busybox:latest" validate_container_image "${CONTAINER_IMAGE}" aws batch register-job-definition \ --job-definition-name "${JOB_DEF_NAME}" \ --type container \ --platform-capabilities FARGATE \ --container-properties "{ \"image\": \"${CONTAINER_IMAGE}\", \"resourceRequirements\": [ {\"type\": \"VCPU\", \"value\": \"0.25\"}, {\"type\": \"MEMORY\", \"value\": \"512\"} ], \"command\": [\"echo\", \"hello world\"], \"networkConfiguration\": { \"assignPublicIp\": \"DISABLED\" }, \"executionRoleArn\": \"arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}\" }" log "Job definition created: ${JOB_DEF_NAME}" # Step 5: Submit job log "Step 5: Submitting job..." local JOB_ID JOB_ID=$(aws batch submit-job \ --job-name "${JOB_NAME}" \ --job-queue "${JOB_QUEUE_NAME}" \ --job-definition "${JOB_DEF_NAME}" \ --query 'jobId' \ --output text) if [ -z "${JOB_ID}" ] || [ "${JOB_ID}" = "None" ]; then log "ERROR: Failed to submit job" exit 1 fi log "Job submitted with ID: ${JOB_ID}" # Step 6: Wait for job completion and view output log "Step 6: Waiting for job completion..." if ! wait_for_job "${JOB_ID}"; then log "ERROR: Job failed or timed out" exit 1 fi # Get log stream name log "Getting job logs..." local LOG_STREAM LOG_STREAM=$(aws batch describe-jobs \ --jobs "${JOB_ID}" \ --query 'jobs[0].attempts[0].taskProperties.containers[0].logStreamName' \ --output text) if [ "${LOG_STREAM}" != "None" ] && [ "${LOG_STREAM}" != "null" ] && [ -n "${LOG_STREAM}" ]; then log "Log stream: ${LOG_STREAM}" log "Job output:" aws logs get-log-events \ --log-group-name "/aws/batch/job" \ --log-stream-name "${LOG_STREAM}" \ --query 'events[*].message' \ --output text 2>/dev/null | tee -a "${LOG_FILE}" || true else log "No log stream available for job" fi log "Tutorial completed successfully!" # Show created resources echo "" echo "===========================================" echo "CREATED RESOURCES" echo "===========================================" echo "The following resources were created:" for resource in "${CREATED_RESOURCES[@]}"; do echo " - ${resource}" done echo "" echo "===========================================" echo "CLEANUP" echo "===========================================" cleanup_resources log "All resources have been cleaned up" } # Run main function main "$@"