├── LICENSE ├── README.md ├── appspec.yml └── script ├── change_permissions.sh ├── common_functions.sh ├── deploy_laravel.sh ├── deregister_from_elb.sh ├── install_dependencies.sh ├── register_with_elb.sh ├── start_server.sh └── stop_server.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Piethein Strengholt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel 5 CodePipeline & CodeDeploy Template 2 | > Piethein Strengholt (2017) 3 | 4 | ## About 5 | An example template for deploying Laravel applications with AWS CodePipeline across an autoscaling group. 6 | For more information see this tutorial: http://docs.aws.amazon.com/codepipeline/latest/userguide/getting-started-w.html 7 | 8 | ## deploy_laravel script 9 | The deploy_laravel script takes care of the Laravel deployment. You might want to replace the git url with your own url. Additionally you might want to replace the sed arguments used to configure the environment script. At the moment it is not possible to make these arguments dynamic (see below). 10 | 11 | ## AWS variables 12 | Currently it is not possible to pass AWS variables into buildspec.yml from CodePipeline. See more info: 13 | https://stackoverflow.com/questions/41704517/aws-pass-in-variable-into-buildspec-yml-from-codepipeline 14 | 15 | As an alternative you can consider the JSON snippet below for your CloudFormation script. The DBHost, DBName, DBUsername and DBPassword varables refer to your database parameters, which should also be defined in your CloudFormation script. 16 | ``` 17 | "files" : { 18 | "/var/www/.env" : { 19 | "content" : { "Fn::Join" : ["", [ 20 | "APP_ENV=local\n", 21 | "APP_DEBUG=true\n", 22 | "APP_KEY=base64:l6kg/jw8Bk1c70FztrJfhXz9mqocYp+aHT1F7JahjxQ=\n", 23 | "\n", 24 | "APP_URL=http://localhost\n", 25 | "SESSION_DOMAIN=localhost\n", 26 | "\n", 27 | "DB_CONNECTION=mysql\n", 28 | "DB_HOST=", { "Ref" : "DBHost" }, "\n", 29 | "DB_PORT=3306\n", 30 | "DB_DATABASE=", { "Ref" : "DBName" }, "\n", 31 | "DB_USERNAME=", { "Ref" : "DBUsername" }, "\n", 32 | "DB_PASSWORD=", { "Ref" : "DBPassword" }, "\n", 33 | "\n", 34 | "CACHE_DRIVER=file\n", 35 | "SESSION_DRIVER=database\n", 36 | "QUEUE_DRIVER=sync\n" 37 | ]]}, 38 | "mode" : "000644", 39 | "owner" : "root", 40 | "group" : "root" 41 | } 42 | } 43 | ``` 44 | 45 | ## Deploy key 46 | If you want to clone a private repository and want to use a deploy key you can consider doing the following. Type the following command: 47 | 48 | ``` 49 | ssh-keygen -t rsa -C "your_email@youremail.com" 50 | ``` 51 | 52 | This will generate a public and a private key. It is best to not type a password, since we don't want to interrupt the deployment process. Add the public key to your GitHub Deploy keys section under settings. The private key should be packaged and added to the appspec.yml file and used during the deployment. See more information here: 53 | http://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-files.html 54 | 55 | Next step is the following commands. This will start the ssh-agent, trust github and import the private key for your project. You might want to change the location of the rsa file and need to type in your github repository location. 56 | 57 | ``` 58 | eval `ssh-agent -s` 59 | echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 60 | ssh-agent bash -c 'ssh-add /root/id_rsa_file; git clone https://github.com:user/project.git' 61 | ``` 62 | 63 | When using the steps above, the deployment will automatically run. 64 | 65 | 66 | ## Access to the AutoScaling API 67 | Make sure the EC2 instances have access to the AutoScaling API. You can achieve this by adding the following policy: 68 | 69 | ``` 70 | { 71 | "Statement": [ 72 | { 73 | "Effect": "Allow", 74 | "Action": [ 75 | "autoscaling:Describe*", 76 | "autoscaling:EnterStandby", 77 | "autoscaling:ExitStandby", 78 | "cloudformation:Describe*", 79 | "cloudformation:GetTemplate", 80 | "s3:Get*" 81 | ], 82 | "Resource": "*" 83 | } 84 | ] 85 | } 86 | ``` 87 | 88 | See more information here: https://aws.amazon.com/blogs/devops/use-aws-codedeploy-to-deploy-to-amazon-ec2-instances-behind-an-elastic-load-balancer-2/ 89 | -------------------------------------------------------------------------------- /appspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.0 2 | os: linux 3 | files: 4 | - source: / 5 | destination: /var/www/html/ 6 | hooks: 7 | BeforeInstall: 8 | - location: scripts/install_dependencies.sh 9 | timeout: 300 10 | runas: root 11 | AfterInstall: 12 | - location: scripts/deploy_laravel.sh 13 | timeout: 300 14 | runas: root 15 | - location: scripts/change_permissions.sh 16 | timeout: 300 17 | runas: root 18 | ApplicationStart: 19 | - location: scripts/start_server.sh 20 | timeout: 120 21 | runas: root 22 | ApplicationStop: 23 | - location: scripts/stop_server.sh 24 | timeout: 120 25 | runas: root 26 | -------------------------------------------------------------------------------- /script/change_permissions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fix user rights 4 | sudo usermod -a -G apache ec2-user 5 | sudo chown -R ec2-user:apache /var/www 6 | sudo chmod 2775 /var/www 7 | find /var/www -type d -exec sudo chmod 2775 {} \; 8 | find /var/www -type f -exec sudo chmod 0664 {} \; 9 | -------------------------------------------------------------------------------- /script/common_functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # A copy of the License is located at 8 | # 9 | # http://aws.amazon.com/apache2.0 10 | # 11 | # or in the "license" file accompanying this file. This file is distributed 12 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | # express or implied. See the License for the specific language governing 14 | # permissions and limitations under the License. 15 | 16 | # ELB_LIST defines which Elastic Load Balancers this instance should be part of. 17 | # The elements in ELB_LIST should be separated by space. Safe default is "". 18 | # Set to "_all_" to automatically find all load balancers the instance is registered to. 19 | # Set to "_any_" will work as "_all_" but will not fail if instance is not attached to 20 | # any ASG or ELB, giving flexibility. 21 | ELB_LIST="" 22 | 23 | # Under normal circumstances, you shouldn't need to change anything below this line. 24 | # ----------------------------------------------------------------------------- 25 | 26 | export PATH="$PATH:/usr/bin:/usr/local/bin" 27 | 28 | # If true, all messages will be printed. If false, only fatal errors are printed. 29 | DEBUG=true 30 | 31 | # If true, all commands will have a initial jitter - use this if deploying to significant number of instances only 32 | INITIAL_JITTER=false 33 | 34 | # Number of times to check for a resouce to be in the desired state. 35 | WAITER_ATTEMPTS=60 36 | 37 | # Number of seconds to wait between attempts for resource to be in a state. 38 | WAITER_INTERVAL=3 39 | 40 | # AutoScaling Standby features at minimum require this version to work. 41 | MIN_CLI_VERSION='1.3.25' 42 | 43 | # Create a flagfile for each deployment 44 | FLAGFILE="/tmp/asg_codedeploy_flags-$DEPLOYMENT_GROUP_ID-$DEPLOYMENT_ID" 45 | 46 | # Handle ASG processes 47 | HANDLE_PROCS=false 48 | 49 | # 50 | # Performs CLI command and provides expotential backoff with Jitter between any failed CLI commands 51 | # FullJitter algorithm taken from: https://www.awsarchitectureblog.com/2015/03/backoff.html 52 | # Optional pre-jitter can be enabled via GLOBAL var INITIAL_JITTER (set to "true" to enable) 53 | # 54 | exec_with_fulljitter_retry() { 55 | local MAX_RETRIES=${EXPBACKOFF_MAX_RETRIES:-8} # Max number of retries 56 | local BASE=${EXPBACKOFF_BASE:-2} # Base value for backoff calculation 57 | local MAX=${EXPBACKOFF_MAX:-120} # Max value for backoff calculation 58 | local FAILURES=0 59 | local RESP 60 | 61 | # Perform initial jitter sleep if enabled 62 | if [ "$INITIAL_JITTER" = "true" ]; then 63 | local SECONDS=$(( $RANDOM % ( ($BASE * 2) ** 2 ) )) 64 | sleep $SECONDS 65 | fi 66 | 67 | # Execute Provided Command 68 | RESP=$(eval $@) 69 | until [ $? -eq 0 ]; do 70 | FAILURES=$(( $FAILURES + 1 )) 71 | if (( $FAILURES > $MAX_RETRIES )); then 72 | echo "$@" >&2 73 | echo " * Failed, max retries exceeded" >&2 74 | return 1 75 | else 76 | local SECONDS=$(( $RANDOM % ( ($BASE * 2) ** $FAILURES ) )) 77 | if (( $SECONDS > $MAX )); then 78 | SECONDS=$MAX 79 | fi 80 | 81 | echo "$@" >&2 82 | echo " * $FAILURES failure(s), retrying in $SECONDS second(s)" >&2 83 | sleep $SECONDS 84 | 85 | # Re-Execute provided command 86 | RESP=$(eval $@) 87 | fi 88 | done 89 | 90 | # Echo out CLI response which is captured by calling function 91 | echo $RESP 92 | return 0 93 | } 94 | 95 | # Usage: get_instance_region 96 | # 97 | # Writes to STDOUT the AWS region as known by the local instance. 98 | get_instance_region() { 99 | if [ -z "$AWS_REGION" ]; then 100 | AWS_REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document \ 101 | | grep -i region \ 102 | | awk -F\" '{print $4}') 103 | fi 104 | 105 | echo $AWS_REGION 106 | } 107 | 108 | AWS_CLI="exec_with_fulljitter_retry aws --region $(get_instance_region)" 109 | # Usage: set_flag 110 | # 111 | # Writes = to FLAGFILE 112 | set_flag() { 113 | if echo "$1=$2" >> $FLAGFILE; then 114 | return 0 115 | else 116 | error_exit "Unable to write flag \"$1=$2\" to $FLAGFILE" 117 | fi 118 | } 119 | 120 | # Usage: get_flag 121 | # 122 | # Checks for in FLAGFILE. Echoes it's value and returns 0 on success or non-zero if it fails to read the file. 123 | get_flag() { 124 | if [ -r $FLAGFILE ]; then 125 | local result=$(awk -F= -v flag="$1" '{if ( $1 == flag ) {print $2}}' $FLAGFILE | tail -1) 126 | echo "${result}" 127 | return 0 128 | else 129 | # FLAGFILE doesn't exist 130 | return 1 131 | fi 132 | } 133 | 134 | # Usage: check_suspended_processes 135 | # 136 | # Checks processes suspended on the ASG before beginning and store them in 137 | # the FLAGFILE to avoid resuming afterwards. Also abort if Launch process 138 | # is suspended. 139 | check_suspended_processes() { 140 | # Get suspended processes in an array 141 | local suspended=($($AWS_CLI autoscaling describe-auto-scaling-groups \ 142 | --auto-scaling-group-name \"${asg_name}\" \ 143 | --query \'AutoScalingGroups[].SuspendedProcesses\' \ 144 | --output text \| awk \'{printf \$1\" \"}\')) 145 | 146 | if [ ${#suspended[@]} -eq 0 ]; then 147 | msg "No processes were suspended on the ASG before starting." 148 | else 149 | msg "This processes were suspended on the ASG before starting: ${suspended[*]}" 150 | fi 151 | 152 | # If "Launch" process is suspended abort because we will not be able to recover from StandBy. Note the "[[ ... =~" bashism. 153 | if [[ "${suspended[@]}" =~ "Launch" ]]; then 154 | error_exit "'Launch' process of AutoScaling is suspended which will not allow us to recover the instance from StandBy. Aborting." 155 | fi 156 | 157 | for process in ${suspended[@]}; do 158 | set_flag "$process" "true" 159 | done 160 | } 161 | 162 | # Usage: suspend_processes 163 | # 164 | # Suspend processes known to cause problems during deployments. 165 | # The API call is idempotent so it doesn't matter if any were previously suspended. 166 | suspend_processes() { 167 | local -a processes=(AZRebalance AlarmNotification ScheduledActions ReplaceUnhealthy) 168 | 169 | msg "Suspending ${processes[*]} processes" 170 | $AWS_CLI autoscaling suspend-processes \ 171 | --auto-scaling-group-name \"${asg_name}\" \ 172 | --scaling-processes ${processes[@]} 173 | if [ $? != 0 ]; then 174 | error_exit "Failed to suspend ${processes[*]} processes for ASG ${asg_name}. Aborting as this may cause issues." 175 | fi 176 | } 177 | 178 | # Usage: resume_processes 179 | # 180 | # Resume processes suspended, except for the one suspended before deployment. 181 | resume_processes() { 182 | local -a processes=(AZRebalance AlarmNotification ScheduledActions ReplaceUnhealthy) 183 | local -a to_resume 184 | 185 | for p in ${processes[@]}; do 186 | if ! local tmp_flag_value=$(get_flag "$p"); then 187 | error_exit "$FLAGFILE doesn't exist or is unreadable" 188 | elif [ ! "$tmp_flag_value" = "true" ] ; then 189 | to_resume=("${to_resume[@]}" "$p") 190 | fi 191 | done 192 | 193 | msg "Resuming ${to_resume[*]} processes" 194 | $AWS_CLI autoscaling resume-processes \ 195 | --auto-scaling-group-name "${asg_name}" \ 196 | --scaling-processes ${to_resume[@]} 197 | if [ $? != 0 ]; then 198 | error_exit "Failed to resume ${to_resume[*]} processes for ASG ${asg_name}. Aborting as this may cause issues." 199 | fi 200 | } 201 | 202 | # Usage: remove_flagfile 203 | # 204 | # Removes FLAGFILE. Returns non-zero if failure. 205 | remove_flagfile() { 206 | if rm $FLAGFILE; then 207 | msg "Successfully removed flagfile $FLAGFILE" 208 | return 0 209 | else 210 | msg "WARNING: Failed to remove flagfile $FLAGFILE." 211 | fi 212 | } 213 | 214 | # Usage: finish_msg 215 | # 216 | # Prints some finishing statistics 217 | finish_msg() { 218 | msg "Finished $(basename $0) at $(/bin/date "+%F %T")" 219 | 220 | end_sec=$(/bin/date +%s.%N) 221 | elapsed_seconds=$(echo "$end_sec" "$start_sec" | awk '{ print $1 - $2 }') 222 | 223 | msg "Elapsed time: $elapsed_seconds" 224 | } 225 | 226 | # Usage: autoscaling_group_name 227 | # 228 | # Prints to STDOUT the name of the AutoScaling group this instance is a part of and returns 0. If 229 | # it is not part of any groups, then it prints nothing. On error calling autoscaling, returns 230 | # non-zero. 231 | autoscaling_group_name() { 232 | local instance_id=$1 233 | 234 | # This operates under the assumption that instances are only ever part of a single ASG. 235 | local autoscaling_name=$($AWS_CLI autoscaling describe-auto-scaling-instances \ 236 | --instance-ids $instance_id \ 237 | --output text \ 238 | --query AutoScalingInstances[0].AutoScalingGroupName | tr -d '\n\r') 239 | 240 | if [ $? != 0 ]; then 241 | return 1 242 | elif [ "$autoscaling_name" == "None" ]; then 243 | echo "" 244 | else 245 | echo "${autoscaling_name}" 246 | fi 247 | 248 | return 0 249 | } 250 | 251 | # Usage: autoscaling_enter_standby 252 | # 253 | # Move into the Standby state in AutoScaling group . Doing so will 254 | # pull it out of any Elastic Load Balancer that might be in front of the group. 255 | # 256 | # Returns 0 if the instance was successfully moved to standby. Non-zero otherwise. 257 | autoscaling_enter_standby() { 258 | local instance_id=$1 259 | local asg_name=${2} 260 | 261 | msg "Checking if this instance has already been moved in the Standby state" 262 | local instance_state=$(get_instance_state_asg $instance_id) 263 | if [ $? != 0 ]; then 264 | msg "Unable to get this instance's lifecycle state." 265 | return 1 266 | fi 267 | 268 | if [ "$instance_state" == "Standby" ]; then 269 | msg "Instance is already in Standby; nothing to do." 270 | return 0 271 | fi 272 | 273 | if [ "$instance_state" == "Pending:Wait" ]; then 274 | msg "Instance is Pending:Wait; nothing to do." 275 | return 0 276 | fi 277 | 278 | if [ "$HANDLE_PROCS" = "true" ]; then 279 | msg "Checking ASG ${asg_name} suspended processes" 280 | check_suspended_processes 281 | 282 | # Suspend troublesome processes while deploying 283 | suspend_processes 284 | fi 285 | 286 | msg "Checking to see if ASG ${asg_name} will let us decrease desired capacity" 287 | local min_desired=$($AWS_CLI autoscaling describe-auto-scaling-groups \ 288 | --auto-scaling-group-name "${asg_name}" \ 289 | --query \'AutoScalingGroups[0].[MinSize, DesiredCapacity]\' \ 290 | --output text) 291 | 292 | local min_cap=$(echo $min_desired | awk '{print $1}') 293 | local desired_cap=$(echo $min_desired | awk '{print $2}') 294 | 295 | if [ -z "$min_cap" -o -z "$desired_cap" ]; then 296 | msg "Unable to determine minimum and desired capacity for ASG ${asg_name}." 297 | msg "Attempting to put this instance into standby regardless." 298 | set_flag "asgmindecremented" "false" 299 | elif [ $min_cap == $desired_cap -a $min_cap -gt 0 ]; then 300 | local new_min=$(($min_cap - 1)) 301 | msg "Decrementing ASG ${asg_name}'s minimum size to $new_min" 302 | msg $($AWS_CLI autoscaling update-auto-scaling-group \ 303 | --auto-scaling-group-name \"${asg_name}\" \ 304 | --min-size $new_min) 305 | if [ $? != 0 ]; then 306 | msg "Failed to reduce ASG ${asg_name}'s minimum size to $new_min. Cannot put this instance into Standby." 307 | return 1 308 | else 309 | msg "ASG ${asg_name}'s minimum size has been decremented, creating flag in file $FLAGFILE" 310 | # Create a "flag" denote that the ASG min has been decremented 311 | set_flag "asgmindecremented" "true" 312 | fi 313 | else 314 | msg "No need to decrement ASG ${asg_name}'s minimum size" 315 | set_flag "asgmindecremented" "false" 316 | fi 317 | 318 | msg "Putting instance $instance_id into Standby" 319 | $AWS_CLI autoscaling enter-standby \ 320 | --instance-ids $instance_id \ 321 | --auto-scaling-group-name \"${asg_name}\" \ 322 | --should-decrement-desired-capacity 323 | if [ $? != 0 ]; then 324 | msg "Failed to put instance $instance_id into Standby for ASG ${asg_name}." 325 | return 1 326 | fi 327 | 328 | msg "Waiting for move to Standby to finish" 329 | wait_for_state "autoscaling" $instance_id "Standby" 330 | if [ $? != 0 ]; then 331 | local wait_timeout=$(($WAITER_INTERVAL * $WAITER_ATTEMPTS)) 332 | msg "Instance $instance_id did not make it to standby after $wait_timeout seconds" 333 | return 1 334 | fi 335 | 336 | return 0 337 | } 338 | 339 | # Usage: autoscaling_exit_standby 340 | # 341 | # Attempts to move instance out of Standby and into InService. Returns 0 if 342 | # successful. 343 | autoscaling_exit_standby() { 344 | local instance_id=$1 345 | local asg_name=${2} 346 | 347 | msg "Checking if this instance has already been moved out of Standby state" 348 | local instance_state=$(get_instance_state_asg $instance_id) 349 | if [ $? != 0 ]; then 350 | msg "Unable to get this instance's lifecycle state." 351 | return 1 352 | fi 353 | 354 | if [ "$instance_state" == "InService" ]; then 355 | msg "Instance is already InService; nothing to do." 356 | return 0 357 | fi 358 | 359 | if [ "$instance_state" == "Pending:Wait" ]; then 360 | msg "Instance is Pending:Wait; nothing to do." 361 | return 0 362 | fi 363 | 364 | msg "Moving instance $instance_id out of Standby" 365 | $AWS_CLI autoscaling exit-standby \ 366 | --instance-ids $instance_id \ 367 | --auto-scaling-group-name \"${asg_name}\" 368 | 369 | if [ $? != 0 ]; then 370 | msg "Failed to put instance $instance_id back into InService for ASG ${asg_name}." 371 | return 1 372 | fi 373 | 374 | msg "Waiting for exit-standby to finish" 375 | wait_for_state "autoscaling" $instance_id "InService" 376 | if [ $? != 0 ]; then 377 | local wait_timeout=$(($WAITER_INTERVAL * $WAITER_ATTEMPTS)) 378 | msg "Instance $instance_id did not make it to InService after $wait_timeout seconds" 379 | return 1 380 | fi 381 | 382 | if ! local tmp_flag_value=$(get_flag "asgmindecremented"); then 383 | error_exit "$FLAGFILE doesn't exist or is unreadable" 384 | elif [ "$tmp_flag_value" = "true" ]; then 385 | local min_desired=$($AWS_CLI autoscaling describe-auto-scaling-groups \ 386 | --auto-scaling-group-name \"${asg_name}\" \ 387 | --query \'AutoScalingGroups[0].[MinSize, DesiredCapacity]\' \ 388 | --output text) 389 | 390 | local min_cap=$(echo $min_desired | awk '{print $1}') 391 | 392 | local new_min=$(($min_cap + 1)) 393 | msg "Incrementing ASG ${asg_name}'s minimum size to $new_min" 394 | msg $($AWS_CLI autoscaling update-auto-scaling-group \ 395 | --auto-scaling-group-name \"${asg_name}\" \ 396 | --min-size $new_min) 397 | if [ $? != 0 ]; then 398 | msg "Failed to increase ASG ${asg_name}'s minimum size to $new_min." 399 | remove_flagfile 400 | return 1 401 | else 402 | msg "Successfully incremented ASG ${asg_name}'s minimum size" 403 | fi 404 | else 405 | msg "Auto scaling group was not decremented previously, not incrementing min value" 406 | fi 407 | 408 | if [ "$HANDLE_PROCS" = "true" ]; then 409 | # Resume processes, except for the ones suspended before deployment 410 | resume_processes 411 | fi 412 | 413 | # Clean up the FLAGFILE 414 | remove_flagfile 415 | return 0 416 | } 417 | 418 | # Usage: get_instance_state_asg 419 | # 420 | # Gets the state of the given as known by the AutoScaling group it's a part of. 421 | # Health is printed to STDOUT and the function returns 0. Otherwise, no output and return is 422 | # non-zero. 423 | get_instance_state_asg() { 424 | local instance_id=$1 425 | 426 | local state=$($AWS_CLI autoscaling describe-auto-scaling-instances \ 427 | --instance-ids $instance_id \ 428 | --query \"AutoScalingInstances[?InstanceId == \'$instance_id\'].LifecycleState \| [0]\" \ 429 | --output text) 430 | if [ $? != 0 ]; then 431 | return 1 432 | else 433 | echo $state 434 | return 0 435 | fi 436 | } 437 | 438 | # Usage: reset_waiter_timeout 439 | # 440 | # Resets the timeout value to account for the ELB timeout and also connection draining. 441 | reset_waiter_timeout() { 442 | local elb=$1 443 | local state_name=$2 444 | 445 | if [ "$state_name" == "InService" ]; then 446 | 447 | # Wait for a health check to succeed 448 | local elb_info=$($AWS_CLI elb describe-load-balancers \ 449 | --load-balancer-name $elb \ 450 | --query \'LoadBalancerDescriptions[0].HealthCheck.[HealthyThreshold,Interval,Timeout]\' \ 451 | --output text) 452 | 453 | local health_check_threshold=$(echo $elb_info | awk '{print $1}') 454 | local health_check_interval=$(echo $elb_info | awk '{print $2}') 455 | local health_check_timeout=$(echo $elb_info | awk '{print $3}') 456 | local timeout=$((health_check_threshold * (health_check_interval + health_check_timeout))) 457 | 458 | elif [ "$state_name" == "OutOfService" ]; then 459 | 460 | # If connection draining is enabled, wait for connections to drain 461 | local draining_values=$($AWS_CLI elb describe-load-balancer-attributes \ 462 | --load-balancer-name $elb \ 463 | --query \'LoadBalancerAttributes.ConnectionDraining.[Enabled,Timeout]\' \ 464 | --output text) 465 | local draining_enabled=$(echo $draining_values | awk '{print $1}') 466 | local timeout=$(echo $draining_values | awk '{print $2}') 467 | 468 | if [ "$draining_enabled" != "True" ]; then 469 | timeout=0 470 | fi 471 | 472 | else 473 | msg "Unknown state name, '$state_name'"; 474 | return 1; 475 | fi 476 | 477 | # Base register/deregister action may take up to about 30 seconds 478 | timeout=$((timeout + 30)) 479 | 480 | WAITER_ATTEMPTS=$((timeout / WAITER_INTERVAL)) 481 | } 482 | 483 | # Usage: wait_for_state [ELB name] 484 | # 485 | # Waits for the state of to be in as seen by . Returns 0 if 486 | # it successfully made it to that state; non-zero if not. By default, checks $WAITER_ATTEMPTS 487 | # times, every $WAITER_INTERVAL seconds. If giving an [ELB name] to check under, these are reset 488 | # to that ELB's timeout values. 489 | wait_for_state() { 490 | local service=$1 491 | local instance_id=$2 492 | local state_name=$3 493 | local elb=$4 494 | 495 | local instance_state_cmd 496 | if [ "$service" == "elb" ]; then 497 | instance_state_cmd="get_instance_health_elb $instance_id $elb" 498 | reset_waiter_timeout $elb $state_name 499 | if [ $? != 0 ]; then 500 | error_exit "Failed resetting waiter timeout for $elb" 501 | fi 502 | elif [ "$service" == "autoscaling" ]; then 503 | instance_state_cmd="get_instance_state_asg $instance_id" 504 | else 505 | msg "Cannot wait for instance state; unknown service type, '$service'" 506 | return 1 507 | fi 508 | 509 | msg "Checking $WAITER_ATTEMPTS times, every $WAITER_INTERVAL seconds, for instance $instance_id to be in state $state_name" 510 | 511 | local instance_state=$($instance_state_cmd | tr -d '\n\r') 512 | local count=1 513 | 514 | msg "Instance is currently in state: $instance_state" 515 | while [ "$instance_state" != "$state_name" ]; do 516 | if [ $count -ge $WAITER_ATTEMPTS ]; then 517 | local timeout=$(($WAITER_ATTEMPTS * $WAITER_INTERVAL)) 518 | msg "Instance failed to reach state, $state_name within $timeout seconds" 519 | return 1 520 | fi 521 | 522 | sleep $WAITER_INTERVAL 523 | 524 | instance_state=$($instance_state_cmd | tr -d '\n\r') 525 | count=$(($count + 1)) 526 | msg "Instance is currently in state: $instance_state" 527 | done 528 | 529 | return 0 530 | } 531 | 532 | # Usage: get_instance_health_elb 533 | # 534 | # Gets the health of the given as known by . If it's a valid health 535 | # status (one of InService|OutOfService|Unknown), then the health is printed to STDOUT and the 536 | # function returns 0. Otherwise, no output and return is non-zero. 537 | get_instance_health_elb() { 538 | local instance_id=$1 539 | local elb_name=$2 540 | 541 | msg "Checking status of instance '$instance_id' in load balancer '$elb_name'" 542 | 543 | # If describe-instance-health for this instance returns an error, then it's not part of 544 | # this ELB. But, if the call was successful let's still double check that the status is 545 | # valid. 546 | local instance_status=$($AWS_CLI elb describe-instance-health \ 547 | --load-balancer-name $elb_name \ 548 | --instances $instance_id \ 549 | --query \'InstanceStates[].State\' \ 550 | --output text 2>/dev/null) 551 | 552 | if [ $? == 0 ]; then 553 | case "$instance_status" in 554 | InService|OutOfService|Unknown) 555 | echo -n $instance_status 556 | return 0 557 | ;; 558 | *) 559 | msg "Instance '$instance_id' not part of ELB '$elb_name'" 560 | return 1 561 | esac 562 | fi 563 | } 564 | 565 | # Usage: validate_elb 566 | # 567 | # Validates that the Elastic Load Balancer with name exists, is describable, and 568 | # contains as one of its instances. 569 | # 570 | # If any of these checks are false, the function returns non-zero. 571 | validate_elb() { 572 | local instance_id=$1 573 | local elb_name=$2 574 | 575 | # Get the list of active instances for this LB. 576 | local elb_instances=$($AWS_CLI elb describe-load-balancers \ 577 | --load-balancer-name $elb_name \ 578 | --query \'LoadBalancerDescriptions[*].Instances[*].InstanceId\' \ 579 | --output text) 580 | if [ $? != 0 ]; then 581 | msg "Couldn't describe ELB instance named '$elb_name'" 582 | return 1 583 | fi 584 | 585 | msg "Checking health of '$instance_id' as known by ELB '$elb_name'" 586 | local instance_health=$(get_instance_health_elb $instance_id $elb_name) 587 | if [ $? != 0 ]; then 588 | return 1 589 | fi 590 | 591 | return 0 592 | } 593 | 594 | # Usage: get_elb_list 595 | # 596 | # Finds all the ELBs that this instance is registered to. After execution, the variable 597 | # "ELB_LIST" will contain the list of load balancers for the given instance. 598 | # 599 | # If the given instance ID isn't found registered to any ELBs, the function returns non-zero 600 | get_elb_list() { 601 | local instance_id=$1 602 | 603 | local elb_list="" 604 | 605 | elb_list=$($AWS_CLI elb describe-load-balancers \ 606 | --query \"LoadBalancerDescriptions[].[join\(\',\',Instances[?InstanceId==\'$instance_id\'].InstanceId\),LoadBalancerName]\" \ 607 | --output text \| grep $instance_id \| awk \'{ORS=\" \"\;print \$2}\') 608 | 609 | if [ -z "$elb_list" ]; then 610 | return 1 611 | else 612 | msg "Got load balancer list of: $elb_list" 613 | ELB_LIST=$elb_list 614 | return 0 615 | fi 616 | } 617 | 618 | # Usage: deregister_instance 619 | # 620 | # Deregisters from . 621 | deregister_instance() { 622 | local instance_id=$1 623 | local elb_name=$2 624 | 625 | $AWS_CLI elb deregister-instances-from-load-balancer \ 626 | --load-balancer-name $elb_name \ 627 | --instances $instance_id 1> /dev/null 628 | 629 | return $? 630 | } 631 | 632 | # Usage: register_instance 633 | # 634 | # Registers to . 635 | register_instance() { 636 | local instance_id=$1 637 | local elb_name=$2 638 | 639 | $AWS_CLI elb register-instances-with-load-balancer \ 640 | --load-balancer-name $elb_name \ 641 | --instances $instance_id 1> /dev/null 642 | 643 | return $? 644 | } 645 | 646 | # Usage: check_cli_version [version-to-check] [desired version] 647 | # 648 | # Without any arguments, checks that the installed version of the AWS CLI is at least at version 649 | # $MIN_CLI_VERSION. Returns non-zero if the version is not high enough. 650 | check_cli_version() { 651 | if [ -z $1 ]; then 652 | version=$($AWS_CLI --version 2\>\&1 \| cut -f1 -d\' \' \| cut -f2 -d/) 653 | else 654 | version=$1 655 | fi 656 | 657 | if [ -z "$2" ]; then 658 | min_version=$MIN_CLI_VERSION 659 | else 660 | min_version=$2 661 | fi 662 | 663 | x=$(echo $version | cut -f1 -d.) 664 | y=$(echo $version | cut -f2 -d.) 665 | z=$(echo $version | cut -f3 -d.) 666 | 667 | min_x=$(echo $min_version | cut -f1 -d.) 668 | min_y=$(echo $min_version | cut -f2 -d.) 669 | min_z=$(echo $min_version | cut -f3 -d.) 670 | 671 | msg "Checking minimum required CLI version (${min_version}) against installed version ($version)" 672 | 673 | if [ $x -lt $min_x ]; then 674 | return 1 675 | elif [ $y -lt $min_y ]; then 676 | return 1 677 | elif [ $y -gt $min_y ]; then 678 | return 0 679 | elif [ $z -ge $min_z ]; then 680 | return 0 681 | else 682 | return 1 683 | fi 684 | } 685 | 686 | # Usage: msg 687 | # 688 | # Writes to STDERR only if $DEBUG is true, otherwise has no effect. 689 | msg() { 690 | local message=$1 691 | $DEBUG && echo $message 1>&2 692 | } 693 | 694 | # Usage: error_exit 695 | # 696 | # Writes to STDERR as a "fatal" and immediately exits the currently running script. 697 | error_exit() { 698 | local message=$1 699 | 700 | echo "[FATAL] $message" 1>&2 701 | exit 1 702 | } 703 | 704 | # Usage: get_instance_id 705 | # 706 | # Writes to STDOUT the EC2 instance ID for the local instance. Returns non-zero if the local 707 | # instance metadata URL is inaccessible. 708 | get_instance_id() { 709 | curl -s http://169.254.169.254/latest/meta-data/instance-id 710 | return $? 711 | } -------------------------------------------------------------------------------- /script/deploy_laravel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Enter html directory 4 | cd /var/www/html/ 5 | 6 | # Create cache and chmod folders 7 | mkdir -p /var/www/html/bootstrap/cache 8 | mkdir -p /var/www/html/storage/framework/sessions 9 | mkdir -p /var/www/html/storage/framework/views 10 | mkdir -p /var/www/html/storage/framework/cache 11 | mkdir -p /var/www/html/public/files/ 12 | 13 | # Install dependencies 14 | export COMPOSER_ALLOW_SUPERUSER=1 15 | composer install -d /var/www/html/ 16 | 17 | # Copy configuration from /var/www/.env, see README.MD for more information 18 | cp /var/www/.env /var/www/html/.env 19 | 20 | # Migrate all tables 21 | php /var/www/html/artisan migrate 22 | 23 | # Clear any previous cached views 24 | php /var/www/html/artisan config:clear 25 | php /var/www/html/artisan cache:clear 26 | php /var/www/html/artisan view:clear 27 | 28 | # Optimize the application 29 | php /var/www/html/artisan config:cache 30 | php /var/www/html/artisan optimize 31 | #php /var/www/html/artisan route:cache 32 | 33 | # Change rights 34 | chmod 777 -R /var/www/html/bootstrap/cache 35 | chmod 777 -R /var/www/html/storage 36 | chmod 777 -R /var/www/html/public/files/ 37 | 38 | # Bring up application 39 | php /var/www/html/artisan up 40 | -------------------------------------------------------------------------------- /script/deregister_from_elb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # A copy of the License is located at 8 | # 9 | # http://aws.amazon.com/apache2.0 10 | # 11 | # or in the "license" file accompanying this file. This file is distributed 12 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | # express or implied. See the License for the specific language governing 14 | # permissions and limitations under the License. 15 | 16 | . $(dirname $0)/common_functions.sh 17 | 18 | msg "Running AWS CLI with region: $(get_instance_region)" 19 | 20 | # get this instance's ID 21 | INSTANCE_ID=$(get_instance_id) 22 | if [ $? != 0 -o -z "$INSTANCE_ID" ]; then 23 | error_exit "Unable to get this instance's ID; cannot continue." 24 | fi 25 | 26 | # Get current time 27 | msg "Started $(basename $0) at $(/bin/date "+%F %T")" 28 | start_sec=$(/bin/date +%s.%N) 29 | 30 | msg "Checking if instance $INSTANCE_ID is part of an AutoScaling group" 31 | asg=$(autoscaling_group_name $INSTANCE_ID) 32 | if [ $? == 0 -a -n "${asg}" ]; then 33 | msg "Found AutoScaling group for instance $INSTANCE_ID: ${asg}" 34 | 35 | msg "Checking that installed CLI version is at least at version required for AutoScaling Standby" 36 | check_cli_version 37 | if [ $? != 0 ]; then 38 | error_exit "CLI must be at least version ${MIN_CLI_X}.${MIN_CLI_Y}.${MIN_CLI_Z} to work with AutoScaling Standby" 39 | fi 40 | 41 | msg "Attempting to put instance into Standby" 42 | autoscaling_enter_standby $INSTANCE_ID "${asg}" 43 | if [ $? != 0 ]; then 44 | error_exit "Failed to move instance into standby" 45 | else 46 | msg "Instance is in standby" 47 | finish_msg 48 | exit 0 49 | fi 50 | fi 51 | 52 | msg "Instance is not part of an ASG, trying with ELB..." 53 | 54 | set_flag "dereg" "true" 55 | 56 | if [ -z "$ELB_LIST" ]; then 57 | error_exit "ELB_LIST is empty. Must have at least one load balancer to deregister from, or \"_all_\", \"_any_\" values." 58 | elif [ "${ELB_LIST}" = "_all_" ]; then 59 | msg "Automatically finding all the ELBs that this instance is registered to..." 60 | get_elb_list $INSTANCE_ID 61 | if [ $? != 0 ]; then 62 | error_exit "Couldn't find any. Must have at least one load balancer to deregister from." 63 | fi 64 | set_flag "ELBs" "$ELB_LIST" 65 | elif [ "${ELB_LIST}" = "_any_" ]; then 66 | msg "Automatically finding all the ELBs that this instance is registered to..." 67 | get_elb_list $INSTANCE_ID 68 | if [ $? != 0 ]; then 69 | msg "Couldn't find any, but ELB_LIST=any so finishing successfully without deregistering." 70 | set_flag "ELBs" "" 71 | finish_msg 72 | exit 0 73 | fi 74 | set_flag "ELBs" "$ELB_LIST" 75 | fi 76 | 77 | # Loop through all LBs the user set, and attempt to deregister this instance from them. 78 | for elb in $ELB_LIST; do 79 | msg "Checking validity of load balancer named '$elb'" 80 | validate_elb $INSTANCE_ID $elb 81 | if [ $? != 0 ]; then 82 | msg "Error validating $elb; cannot continue with this LB" 83 | continue 84 | fi 85 | 86 | msg "Deregistering $INSTANCE_ID from $elb" 87 | deregister_instance $INSTANCE_ID $elb 88 | 89 | if [ $? != 0 ]; then 90 | error_exit "Failed to deregister instance $INSTANCE_ID from ELB $elb" 91 | fi 92 | done 93 | 94 | # Wait for all deregistrations to finish 95 | msg "Waiting for instance to de-register from its load balancers" 96 | for elb in $ELB_LIST; do 97 | wait_for_state "elb" $INSTANCE_ID "OutOfService" $elb 98 | if [ $? != 0 ]; then 99 | error_exit "Failed waiting for $INSTANCE_ID to leave $elb" 100 | fi 101 | done 102 | 103 | finish_msg -------------------------------------------------------------------------------- /script/install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on error 4 | set -o errexit -o pipefail 5 | 6 | # Update yum 7 | yum update -y 8 | 9 | # Install packages 10 | yum install -y curl 11 | yum install -y git 12 | 13 | # Remove current apache & php 14 | yum -y remove httpd* php* 15 | 16 | # Install PHP 7.1 17 | yum install -y php71 php71-cli php71-fpm php71-mysql php71-xml php71-curl php71-opcache php71-pdo php71-gd php71-pecl-apcu php71-mbstring php71-imap php71-pecl-redis php71-mcrypt php71-mysqlnd mod24_ssl 18 | 19 | # Install Apache 2.4 20 | yum -y install httpd24 21 | 22 | # Allow URL rewrites 23 | sed -i 's#AllowOverride None#AllowOverride All#' /etc/httpd/conf/httpd.conf 24 | 25 | # Change apache document root 26 | mkdir -p /var/www/html/public 27 | sed -i 's#DocumentRoot "/var/www/html"#DocumentRoot "/var/www/html/public"#' /etc/httpd/conf/httpd.conf 28 | 29 | # Change apache directory index 30 | sed -e 's/DirectoryIndex.*/DirectoryIndex index.html index.php/' -i /etc/httpd/conf/httpd.conf 31 | 32 | # Get Composer, and install to /usr/local/bin 33 | if [ ! -f "/usr/local/bin/composer" ]; then 34 | php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 35 | php composer-setup.php --install-dir=/usr/bin --filename=composer 36 | php -r "unlink('composer-setup.php');" 37 | else 38 | /usr/local/bin/composer self-update --stable --no-ansi --no-interaction 39 | fi 40 | 41 | # Restart apache 42 | service httpd start 43 | 44 | # Setup apache to start on boot 45 | chkconfig httpd on 46 | 47 | # Ensure aws-cli is installed and configured 48 | if [ ! -f "/usr/bin/aws" ]; then 49 | curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" 50 | unzip awscli-bundle.zip 51 | ./awscli-bundle/install -b /usr/bin/aws 52 | fi 53 | 54 | # Ensure AWS Variables are available 55 | if [[ -z "$AWS_ACCOUNT_ID" || -z "$AWS_DEFAULT_REGION " ]]; then 56 | echo "AWS Variables Not Set. Either AWS_ACCOUNT_ID or AWS_DEFAULT_REGION" 57 | fi 58 | -------------------------------------------------------------------------------- /script/register_with_elb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # A copy of the License is located at 8 | # 9 | # http://aws.amazon.com/apache2.0 10 | # 11 | # or in the "license" file accompanying this file. This file is distributed 12 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | # express or implied. See the License for the specific language governing 14 | # permissions and limitations under the License. 15 | 16 | . $(dirname $0)/common_functions.sh 17 | 18 | msg "Running AWS CLI with region: $(get_instance_region)" 19 | 20 | # get this instance's ID 21 | INSTANCE_ID=$(get_instance_id) 22 | if [ $? != 0 -o -z "$INSTANCE_ID" ]; then 23 | error_exit "Unable to get this instance's ID; cannot continue." 24 | fi 25 | 26 | # Get current time 27 | msg "Started $(basename $0) at $(/bin/date "+%F %T")" 28 | start_sec=$(/bin/date +%s.%N) 29 | 30 | msg "Checking if instance $INSTANCE_ID is part of an AutoScaling group" 31 | asg=$(autoscaling_group_name $INSTANCE_ID) 32 | if [ $? == 0 -a -n "${asg}" ]; then 33 | msg "Found AutoScaling group for instance $INSTANCE_ID: ${asg}" 34 | 35 | msg "Checking that installed CLI version is at least at version required for AutoScaling Standby" 36 | check_cli_version 37 | if [ $? != 0 ]; then 38 | error_exit "CLI must be at least version ${MIN_CLI_X}.${MIN_CLI_Y}.${MIN_CLI_Z} to work with AutoScaling Standby" 39 | fi 40 | 41 | msg "Attempting to move instance out of Standby" 42 | autoscaling_exit_standby $INSTANCE_ID "${asg}" 43 | if [ $? != 0 ]; then 44 | error_exit "Failed to move instance out of standby" 45 | else 46 | msg "Instance is no longer in Standby" 47 | finish_msg 48 | exit 0 49 | fi 50 | fi 51 | 52 | msg "Instance is not part of an ASG, continuing with ELB" 53 | 54 | if [ -z "$ELB_LIST" ]; then 55 | error_exit "ELB_LIST is empty. Must have at least one load balancer to register to, or \"_all_\", \"_any_\" values." 56 | elif [ "${ELB_LIST}" = "_all_" ]; then 57 | if [ "$(get_flag "dereg")" = "true" ]; then 58 | msg "Finding all the ELBs that this instance was previously registered to" 59 | if ! ELB_LIST=$(get_flag "ELBs"); then 60 | error_exit "$FLAGFILE doesn't exist or is unreadble" 61 | elif [ -z $ELB_LIST ]; then 62 | error_exit "Couldn't find any. Must have at least one load balancer to register to." 63 | fi 64 | else 65 | msg "Assuming this is the first deployment and ELB_LIST=_all_ so finishing successfully without registering." 66 | finish_msg 67 | exit 0 68 | fi 69 | elif [ "${ELB_LIST}" = "_any_" ]; then 70 | if [ "$(get_flag "dereg")" = "true" ]; then 71 | msg "Finding all the ELBs that this instance was previously registered to" 72 | if ! ELB_LIST=$(get_flag "ELBs"); then 73 | error_exit "$FLAGFILE doesn't exist or is unreadble" 74 | elif [ -z $ELB_LIST ]; then 75 | msg "Couldn't find any, but ELB_LIST=_any_ so finishing successfully without registering." 76 | remove_flagfile 77 | finish_msg 78 | exit 0 79 | fi 80 | else 81 | msg "Assuming this is the first deployment and ELB_LIST=_any_ so finishing successfully without registering." 82 | finish_msg 83 | exit 0 84 | fi 85 | fi 86 | 87 | # Loop through all LBs the user set, and attempt to register this instance to them. 88 | for elb in $ELB_LIST; do 89 | msg "Checking validity of load balancer named '$elb'" 90 | validate_elb $INSTANCE_ID $elb 91 | if [ $? != 0 ]; then 92 | msg "Error validating $elb; cannot continue with this LB" 93 | continue 94 | fi 95 | 96 | msg "Registering $INSTANCE_ID to $elb" 97 | register_instance $INSTANCE_ID $elb 98 | 99 | if [ $? != 0 ]; then 100 | error_exit "Failed to register instance $INSTANCE_ID from ELB $elb" 101 | fi 102 | done 103 | 104 | # Wait for all registrations to finish 105 | msg "Waiting for instance to register to its load balancers" 106 | for elb in $ELB_LIST; do 107 | wait_for_state "elb" $INSTANCE_ID "InService" $elb 108 | if [ $? != 0 ]; then 109 | error_exit "Failed waiting for $INSTANCE_ID to return to $elb" 110 | fi 111 | done 112 | 113 | remove_flagfile 114 | 115 | finish_msg -------------------------------------------------------------------------------- /script/start_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service httpd start 3 | -------------------------------------------------------------------------------- /script/stop_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | isExistHttps = `pgrep httpd` 3 | if [[ -n $isExistHttps ]]; then 4 | service httpd stop 5 | fi 6 | --------------------------------------------------------------------------------