Two defaults today, both of them different from what a reasonable person would assume. Compose no longer wants the version key that every tutorial still opens with. And an Auto Scaling group created from the CLI has a health check grace period of zero, where the console gives you 300 seconds.
One is a warning. The other is an infinite loop.
One Docker task, one AWS task. Write a Compose file serving httpd from a host directory, then rebuild Day 36's load-balanced instance as an Auto Scaling group. The tasks come from the KodeKloud Engineer platform.
The version key is obsolete
services:
web:
image: httpd:latest
container_name: httpd
ports:
- "5000:80"
volumes:
- /opt/sysops:/usr/local/apache2/htdocs
No version: "3.9" at the top, and that is deliberate. Compose's documentation says the top-level version property exists only for backward compatibility, that it is informative, and that using it produces a warning telling you it is obsolete. Compose validates against the most recent schema regardless of what you write there.
So the line every older tutorial opens with now buys nothing and costs you a warning on every run.
Three other things in that short file are worth more than they look.
ports and volumes both read host first, container second, matching -p and -v on the command line. Quote the port mapping, because YAML will read an unquoted 5000:80 as a sexagesimal number in some parsers and hand you a value you did not write.
The document root is /usr/local/apache2/htdocs, not /var/www/html. That second path belongs to the Debian and Ubuntu Apache packages; the official httpd image lays itself out differently. Mount over the wrong one and you get a running container cheerfully serving the image's default page.
And container_name fixes the name rather than letting Compose generate <project>-<service>-1. It makes docker exec predictable and makes scaling that service past one replica impossible, since two containers cannot share a name.
The default that would have cost me the task
The AWS half was Day 36 again with the hand-launched instance replaced by an Auto Scaling group, a launch template and a target tracking policy. Two things in it are genuinely worth carrying.
First, launch templates do not encode user data for you.
# run-instances: CLI v2 base64-encodes this automatically
--user-data file:///root/ud.sh
# create-launch-template: you do it yourself
UD=$(base64 -w 0 /root/ud.sh)
run-instances has a dedicated parameter with special handling. create-launch-template takes one opaque blob where UserData is a string field the API expects already encoded. Pass raw text and the call succeeds. The template is created, the ASG launches an instance, cloud-init cannot make sense of the payload and moves on. You get a running instance with no nginx, no error in any API response, and a target group that never goes healthy. The only trace is in a log file on an instance you have not set up SSH for.
Second, and this is the one I would not have found without reading the documentation:
--health-check-grace-period 300
The grace period is the minimum time a new instance stays in service before being terminated for failing a health check. AWS documents the default as 300 seconds in the console and 0 seconds via the CLI or SDK, where 0 turns it off entirely.
That matters because the task also required --health-check-type ELB, which makes the target group's HTTP check drive instance replacement rather than the default EC2 status check. The default only asks whether the VM is running, so nginx could crash completely and the ASG would see a perfectly healthy instance.
Put those together with no grace period and the sequence is:
- Instance launches
- Health check fails, because nginx is still installing
- ASG terminates it as unhealthy
- ASG launches a replacement to satisfy desired capacity
- Back to step 2, forever
An infinite launch-and-terminate loop that burns money and never converges, and it shows up in the ASG activity history rather than anywhere you would think to look. Preventing exactly that is what AWS says the grace period is for.
Worth noting the warning runs the other way too. Set it too high and a genuinely broken instance stays in service longer, blunting the health checks you turned on.
Three smaller things
The ASG registers targets, you do not. Naming --target-group-arns on the group means it registers every instance it launches and deregisters every one it terminates. Running register-targets by hand against an ASG-managed group is actively wrong, because the next scaling action silently undoes it.
Early unhealthy is normal and the reason field tells you which kind it is. Elb.RegistrationInProgress and Elb.InitialHealthChecking mean wait. Target.Timeout means no TCP connection, so a security group or nothing listening. Target.ResponseCodeMismatch means it answered with the wrong status. Only the last two are unambiguously broken.
And escape \$Latest. It is a literal AWS keyword, and unescaped inside double quotes bash expands it as a shell variable, finds nothing, and sends Version= to an API that rejects it.
Reading the default rather than assuming it
The Compose version key is a default that used to be required and now warns. The grace period is a default that differs depending on whether you used the console or the CLI, in a direction that turns a working configuration into a loop.
Neither is discoverable by doing the obvious thing and watching it work. The Compose file runs fine with the version key, just noisily. The ASG would have failed in a way that looks like an application problem.
So here is the Day 44 question. For the last resource you created from a script rather than a console, do you know which of its defaults differ between the two?
Day 44 down. Fifty-six to go.
Top comments (0)