|
| 1 | +# Local AMI Development with pg-ami-builder |
| 2 | + |
| 3 | +This guide explains how to use `pg-ami-builder` for local AMI development and iteration. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +### Required Tools |
| 8 | + |
| 9 | +- AWS CLI v2 |
| 10 | +- aws-vault (for credential management) |
| 11 | +- SSM Session Manager plugin |
| 12 | +- Git |
| 13 | +- Nix |
| 14 | + |
| 15 | +### Installing SSM Session Manager Plugin |
| 16 | + |
| 17 | +**macOS:** |
| 18 | +```bash |
| 19 | +brew install --cask session-manager-plugin |
| 20 | +``` |
| 21 | + |
| 22 | +**Linux:** |
| 23 | +See [AWS documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) |
| 24 | + |
| 25 | +### AWS Permissions |
| 26 | + |
| 27 | +Your AWS user/role needs these permissions: |
| 28 | +- EC2: RunInstances, TerminateInstances, DescribeInstances, CreateTags |
| 29 | +- EC2: CreateSecurityGroup, DeleteSecurityGroup, AuthorizeSecurityGroupIngress |
| 30 | +- SSM: StartSession, DescribeSessions |
| 31 | +- EC2: CreateImage, DescribeImages (if using --create-ami) |
| 32 | + |
| 33 | +## Quick Start |
| 34 | + |
| 35 | +### Building Phase 1 |
| 36 | + |
| 37 | +```bash |
| 38 | +# Run phase 1 build (launches instance and runs packer build) |
| 39 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15 |
| 40 | + |
| 41 | +# If packer build fails, instance stays alive for debugging |
| 42 | +# SSH to investigate |
| 43 | +aws-vault exec dev -- nix run .#pg-ami-builder -- ssh |
| 44 | + |
| 45 | +# Make local changes and re-run with file sync |
| 46 | +vim ansible/playbook.yml |
| 47 | +aws-vault exec dev -- nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files |
| 48 | + |
| 49 | +# Cleanup when done |
| 50 | +aws-vault exec dev -- nix run .#pg-ami-builder -- cleanup |
| 51 | +``` |
| 52 | + |
| 53 | +### Building Phase 2 |
| 54 | + |
| 55 | +```bash |
| 56 | +# Run phase 2 with existing stage-1 AMI |
| 57 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase2 \ |
| 58 | + --source-ami ami-stage1-xyz \ |
| 59 | + --postgres-version 15 |
| 60 | +``` |
| 61 | + |
| 62 | +## Commands |
| 63 | + |
| 64 | +### build phase1 |
| 65 | + |
| 66 | +Launch EC2 instance and run phase 1 ansible playbook. |
| 67 | + |
| 68 | +```bash |
| 69 | +nix run .#pg-ami-builder -- build phase1 --postgres-version 15 [flags] |
| 70 | +``` |
| 71 | + |
| 72 | +**Flags:** |
| 73 | +- `--postgres-version` (required) - PostgreSQL major version (15, 16, 17) |
| 74 | +- `--region` - AWS region (default: us-east-1) |
| 75 | +- `--create-ami` - Create AMI on success (default: false) |
| 76 | +- `--ansible-args` - Additional ansible arguments |
| 77 | +- `--instance-type` - EC2 instance type (default: c6g.4xlarge) |
| 78 | +- `--state-file` - Custom state file path |
| 79 | + |
| 80 | +### build phase2 |
| 81 | + |
| 82 | +Launch EC2 instance from stage-1 AMI and run phase 2 ansible playbook. |
| 83 | + |
| 84 | +```bash |
| 85 | +nix run .#pg-ami-builder -- build phase2 --source-ami ami-xyz --postgres-version 15 [flags] |
| 86 | +``` |
| 87 | + |
| 88 | +**Flags:** |
| 89 | +- `--source-ami` (required) - Stage-1 AMI ID |
| 90 | +- `--postgres-version` (required) - PostgreSQL major version |
| 91 | +- `--git-sha` - Git SHA for nix packages (default: current HEAD) |
| 92 | +- Plus all flags from phase1 |
| 93 | + |
| 94 | +### ansible-rerun |
| 95 | + |
| 96 | +Re-run ansible playbook on existing instance. Optionally sync local file changes first. |
| 97 | + |
| 98 | +```bash |
| 99 | +nix run .#pg-ami-builder -- ansible-rerun phase1 [flags] |
| 100 | +``` |
| 101 | + |
| 102 | +**Flags:** |
| 103 | +- `--instance-id` - Target specific instance (default: from state file) |
| 104 | +- `--sync-files` - Sync local ansible/, scripts/, and migrations/ files before running (default: false) |
| 105 | +- `--ansible-args` - Additional ansible arguments |
| 106 | +- `--skip-tags` - Ansible tags to skip |
| 107 | +- `--region` - AWS region (default: us-east-1) |
| 108 | + |
| 109 | +**Examples:** |
| 110 | + |
| 111 | +```bash |
| 112 | +# Re-run without syncing files (use existing files on instance) |
| 113 | +nix run .#pg-ami-builder -- ansible-rerun phase1 |
| 114 | + |
| 115 | +# Re-run with local file changes |
| 116 | +nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files |
| 117 | + |
| 118 | +# Re-run with skip tags |
| 119 | +nix run .#pg-ami-builder -- ansible-rerun phase1 --skip-tags migrations |
| 120 | +``` |
| 121 | + |
| 122 | +### ssh |
| 123 | + |
| 124 | +Connect to instance via AWS SSM Session Manager (default) or EC2 Instance Connect. |
| 125 | + |
| 126 | +```bash |
| 127 | +nix run .#pg-ami-builder -- ssh [flags] |
| 128 | +``` |
| 129 | + |
| 130 | +**Flags:** |
| 131 | +- `--instance-id` - Target specific instance for SSM (default: from state file) |
| 132 | +- `--region` - AWS region for SSM (default: us-east-1) |
| 133 | +- `--aws-ec2-connect-cmd` - Full AWS EC2 Instance Connect command string |
| 134 | + |
| 135 | +**Examples:** |
| 136 | + |
| 137 | +```bash |
| 138 | +# Connect via SSM (default) |
| 139 | +nix run .#pg-ami-builder -- ssh |
| 140 | + |
| 141 | +# Connect via EC2 Instance Connect |
| 142 | +nix run .#pg-ami-builder -- ssh \ |
| 143 | + --aws-ec2-connect-cmd "aws ec2-instance-connect ssh --instance-id i-024bba2db43e4b41f --region us-east-1" |
| 144 | +``` |
| 145 | + |
| 146 | +### cleanup |
| 147 | + |
| 148 | +Terminate instance and remove associated resources. |
| 149 | + |
| 150 | +```bash |
| 151 | +nix run .#pg-ami-builder -- cleanup [flags] |
| 152 | +``` |
| 153 | + |
| 154 | +**Flags:** |
| 155 | +- `--instance-id` - Target specific instance (default: from state file) |
| 156 | +- `--force` - Skip confirmation prompt |
| 157 | + |
| 158 | +## Workflows |
| 159 | + |
| 160 | +### Workflow 1: Develop and test phase 1 changes |
| 161 | + |
| 162 | +```bash |
| 163 | +# Run phase 1 build (launches instance and runs packer build) |
| 164 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15 |
| 165 | + |
| 166 | +# If packer fails, instance stays up for debugging |
| 167 | +# SSH to investigate |
| 168 | +aws-vault exec dev -- nix run .#pg-ami-builder -- ssh |
| 169 | + |
| 170 | +# Make local changes to ansible files |
| 171 | +vim ansible/playbook.yml |
| 172 | + |
| 173 | +# Re-run with your local changes |
| 174 | +aws-vault exec dev -- nix run .#pg-ami-builder -- ansible-rerun phase1 --sync-files |
| 175 | + |
| 176 | +# Repeat until working, then create AMI |
| 177 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 --postgres-version 15 --create-ami |
| 178 | + |
| 179 | +# Cleanup |
| 180 | +aws-vault exec dev -- nix run .#pg-ami-builder -- cleanup |
| 181 | +``` |
| 182 | + |
| 183 | +### Workflow 2: Parallel builds for multiple postgres versions |
| 184 | + |
| 185 | +```bash |
| 186 | +# Build PG 15 |
| 187 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 \ |
| 188 | + --postgres-version 15 \ |
| 189 | + --state-file ~/.pg-ami-build/pg15.json |
| 190 | + |
| 191 | +# Build PG 16 in parallel |
| 192 | +aws-vault exec dev -- nix run .#pg-ami-builder -- build phase1 \ |
| 193 | + --postgres-version 16 \ |
| 194 | + --state-file ~/.pg-ami-build/pg16.json |
| 195 | + |
| 196 | +# SSH into PG 15 instance |
| 197 | +aws-vault exec dev -- nix run .#pg-ami-builder -- ssh \ |
| 198 | + --state-file ~/.pg-ami-build/pg15.json |
| 199 | +``` |
| 200 | + |
| 201 | +## Troubleshooting |
| 202 | + |
| 203 | +### SSM Connection Fails |
| 204 | + |
| 205 | +1. Check SSM agent status on the instance |
| 206 | +2. Verify instance profile has SSM permissions |
| 207 | +3. Ensure session-manager-plugin is installed |
| 208 | + |
| 209 | +### Ansible Fails |
| 210 | + |
| 211 | +The instance is kept running on failure. Check logs: |
| 212 | + |
| 213 | +```bash |
| 214 | +# SSH into instance |
| 215 | +nix run .#pg-ami-builder -- ssh |
| 216 | + |
| 217 | +# Check ansible logs |
| 218 | +sudo journalctl -u ansible-provisioner |
| 219 | +``` |
| 220 | + |
| 221 | +### State File Issues |
| 222 | + |
| 223 | +If state file references non-existent instance: |
| 224 | + |
| 225 | +```bash |
| 226 | +# Override with specific instance |
| 227 | +nix run .#pg-ami-builder -- ssh --instance-id i-xxxxx |
| 228 | + |
| 229 | +# Or clear state and start fresh |
| 230 | +rm ~/.pg-ami-build/state.json |
| 231 | +``` |
| 232 | + |
| 233 | +## Advanced Usage |
| 234 | + |
| 235 | +### Custom State Files for Parallel Builds |
| 236 | + |
| 237 | +Use `--state-file` to manage multiple builds: |
| 238 | + |
| 239 | +```bash |
| 240 | +nix run .#pg-ami-builder -- build phase1 \ |
| 241 | + --postgres-version 15 \ |
| 242 | + --state-file ~/.pg-ami-build/custom.json |
| 243 | +``` |
| 244 | + |
| 245 | +### Additional Ansible Arguments |
| 246 | + |
| 247 | +Pass custom arguments to ansible: |
| 248 | + |
| 249 | +```bash |
| 250 | +nix run .#pg-ami-builder -- build phase1 \ |
| 251 | + --postgres-version 15 \ |
| 252 | + --ansible-args="--skip-tags=migrations" |
| 253 | +``` |
| 254 | + |
| 255 | +## State File |
| 256 | + |
| 257 | +Location: `~/.pg-ami-build/state.json` |
| 258 | + |
| 259 | +The state file tracks the current build instance, allowing subsequent commands to operate on the same instance without specifying `--instance-id`. |
| 260 | + |
| 261 | +Example state: |
| 262 | +```json |
| 263 | +{ |
| 264 | + "instance_id": "i-1234567890abcdef0", |
| 265 | + "phase": "phase1", |
| 266 | + "execution_id": "1731672000-15", |
| 267 | + "region": "us-east-1", |
| 268 | + "postgres_version": "15", |
| 269 | + "timestamp": "2025-11-15T10:30:00Z", |
| 270 | + "git_sha": "abc123def456" |
| 271 | +} |
| 272 | +``` |
0 commit comments