diff options
author | Jake Buchholz <tomalok@gmail.com> | 2018-08-27 22:26:28 -0700 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2018-08-28 09:20:48 -0700 |
commit | 95b7837c9fc2b070bc9e44da1f6fdc0987eabf75 (patch) | |
tree | deb48b1283462997d4039f641ff8a2af44057133 | |
parent | 356105f23d0dc8cc50e629624892b223f209de37 (diff) | |
download | alpine-ec2-ami-95b7837c9fc2b070bc9e44da1f6fdc0987eabf75.tar.bz2 alpine-ec2-ami-95b7837c9fc2b070bc9e44da1f6fdc0987eabf75.tar.xz alpine-ec2-ami-95b7837c9fc2b070bc9e44da1f6fdc0987eabf75.zip |
PR updates
* README.md
+ update list of modern instance types
+ add caveat regarding linux-vanilla vs. linux-virt
* alpine-ami.yaml
+ build instance type is always t3.nano
+ block device where we build is always /dev/xvdf
+ add optional AMI encryption
+ always enable AMI SR-IOV flag (vanilla & virt both have the necessary driver)
+ no need to pass volume_name to make_ami.sh
* make_ami.sh
+ replace hard tabs with 4 spaces
+ always set up edge repositories
+ no need to add mkinitfs package, it's a dependency of linux-*
+ fix update of /etc/inittab
+ fix configuration of NTP
+ declare local vars in main()
+ device is always /dev/xvdf
* variables.json-default/example
+ improve comment for kernel_flavor
+ default add_repos is now empty
+ remove acct & e2fsprogs-extra from add_pkgs
+ add optional AMI encryption
+ remove sriov_enable, build_instance_type, and volume_name vars
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | alpine-ami.yaml | 13 | ||||
-rwxr-xr-x | make_ami.sh | 423 | ||||
-rw-r--r-- | variables.json-default | 10 | ||||
-rw-r--r-- | variables.json-example | 43 |
5 files changed, 254 insertions, 248 deletions
@@ -8,8 +8,8 @@ containing Alpine Linux. The AMI is designed to work with most EC2 features | |||
8 | such as Elastic Network Adapters and NVME EBS volumes by default. If anything | 8 | such as Elastic Network Adapters and NVME EBS volumes by default. If anything |
9 | is missing please report a bug. | 9 | is missing please report a bug. |
10 | 10 | ||
11 | This image can be launched on any modern instance type. Including T2, M5, C5, | 11 | This image can be launched on any modern instance type, including T3, M5, C5, |
12 | I3, R4, P2, P3, X1, X1e, D2. Other instances may also work but have not been | 12 | I3, R5, P3, X1, X1e, D2, Z1d. Other instances may also work but have not been |
13 | tested. If you find an issue with instance support for any current generation | 13 | tested. If you find an issue with instance support for any current generation |
14 | instance please file a bug against this project. | 14 | instance please file a bug against this project. |
15 | 15 | ||
@@ -50,10 +50,15 @@ its development and thus there are some sharp edges. | |||
50 | hardware so it seems unlikely that they will be supported going forward. Thus | 50 | hardware so it seems unlikely that they will be supported going forward. Thus |
51 | this project does not support them. | 51 | this project does not support them. |
52 | 52 | ||
53 | - The linux-vanilla kernel all the linux-firmware packages it installs is much | ||
54 | larger than is necessary for an AMI designed to run on EC2. Unfortunately, | ||
55 | the linux-virt kernel is currently missing NVMe support, which is required for | ||
56 | the newest generation of instance families. | ||
57 | |||
53 | - The aws-ena-driver-vanilla package is still in edge/testing, and requires the | 58 | - The aws-ena-driver-vanilla package is still in edge/testing, and requires the |
54 | matching linux-vanilla package from edge/main. When ENA is available in an | 59 | matching linux-vanilla package from edge/main. When ENA is available in an |
55 | alpine version release, edge/testing and edge/main should no longer be | 60 | alpine version release (ideally with a 'virt' kernel flavor), edge/testing |
56 | necessary. | 61 | and edge/main should no longer be necessary. |
57 | 62 | ||
58 | - [cloud-init](https://cloudinit.readthedocs.io/en/latest/) is not currently | 63 | - [cloud-init](https://cloudinit.readthedocs.io/en/latest/) is not currently |
59 | supported on Alpine Linux. Instead this image uses | 64 | supported on Alpine Linux. Instead this image uses |
diff --git a/alpine-ami.yaml b/alpine-ami.yaml index bc39b89..09e0c4d 100644 --- a/alpine-ami.yaml +++ b/alpine-ami.yaml | |||
@@ -1,6 +1,6 @@ | |||
1 | variables: | 1 | variables: |
2 | 2 | ||
3 | # NOTE: Additional configuration is set via the `variables.json` file. | 3 | # NOTE: Configuration is done with a `variables.json` file. |
4 | # To use default values, simply `cp variables.json-default variables.json`. | 4 | # To use default values, simply `cp variables.json-default variables.json`. |
5 | # See `variables.json-example` for full configuration variable descriptions. | 5 | # See `variables.json-example` for full configuration variable descriptions. |
6 | 6 | ||
@@ -17,11 +17,11 @@ builders: | |||
17 | vpc_id: "{{user `vpc`}}" | 17 | vpc_id: "{{user `vpc`}}" |
18 | subnet_id: "{{user `subnet`}}" | 18 | subnet_id: "{{user `subnet`}}" |
19 | security_group_id: "{{user `security_group`}}" | 19 | security_group_id: "{{user `security_group`}}" |
20 | instance_type: "{{user `build_instance_type`}}" | 20 | instance_type: "t3.nano" |
21 | associate_public_ip_address: "{{user `public_ip`}}" | 21 | associate_public_ip_address: "{{user `public_ip`}}" |
22 | launch_block_device_mappings: | 22 | launch_block_device_mappings: |
23 | - volume_type: "gp2" | 23 | - volume_type: "gp2" |
24 | device_name: "{{user `volume_name`}}" | 24 | device_name: "/dev/xvdf" |
25 | delete_on_termination: "true" | 25 | delete_on_termination: "true" |
26 | volume_size: "{{user `volume_size`}}" | 26 | volume_size: "{{user `volume_size`}}" |
27 | ssh_username: "ec2-user" | 27 | ssh_username: "ec2-user" |
@@ -42,13 +42,14 @@ builders: | |||
42 | ami_description: "{{user `ami_desc_prefix`}}{{user `alpine_release`}}-r{{user `ami_release`}}{{user `ami_desc_suffix`}}" | 42 | ami_description: "{{user `ami_desc_prefix`}}{{user `alpine_release`}}-r{{user `ami_release`}}{{user `ami_desc_suffix`}}" |
43 | ami_virtualization_type: "hvm" | 43 | ami_virtualization_type: "hvm" |
44 | ami_root_device: | 44 | ami_root_device: |
45 | source_device_name: "{{user `volume_name`}}" | 45 | source_device_name: "/dev/xvdf" |
46 | device_name: "/dev/xvda" | 46 | device_name: "/dev/xvda" |
47 | delete_on_termination: "true" | 47 | delete_on_termination: "true" |
48 | volume_size: "{{user `volume_size`}}" | 48 | volume_size: "{{user `volume_size`}}" |
49 | volume_type: "gp2" | 49 | volume_type: "gp2" |
50 | encrypt_boot: "{{user `encrypt_ami`}}" | ||
50 | ena_support: "{{user `ena_enable`}}" | 51 | ena_support: "{{user `ena_enable`}}" |
51 | sriov_support: "{{user `sriov_enable`}}" | 52 | sriov_support: "true" |
52 | ami_groups: "{{user `ami_access`}}" | 53 | ami_groups: "{{user `ami_access`}}" |
53 | ami_regions: "{{user `deploy_regions`}}" | 54 | ami_regions: "{{user `deploy_regions`}}" |
54 | 55 | ||
@@ -56,4 +57,4 @@ builders: | |||
56 | provisioners: | 57 | provisioners: |
57 | - type: "shell" | 58 | - type: "shell" |
58 | script: "make_ami.sh" | 59 | script: "make_ami.sh" |
59 | execute_command: 'sudo sh -c "{{ .Vars }} {{ .Path }} {{user `volume_name`}} {{user `kernel_flavor`}} ''{{user `add_repos`}}'' ''{{user `add_pkgs`}}''"' | 60 | execute_command: 'sudo sh -c "{{ .Vars }} {{ .Path }} {{user `kernel_flavor`}} ''{{user `add_repos`}}'' ''{{user `add_pkgs`}}''"' |
diff --git a/make_ami.sh b/make_ami.sh index 2917445..6218694 100755 --- a/make_ami.sh +++ b/make_ami.sh | |||
@@ -1,5 +1,5 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | # vim: set ts=4 noet: | 2 | # vim: set ts=4 et: |
3 | 3 | ||
4 | set -eu | 4 | set -eu |
5 | 5 | ||
@@ -10,308 +10,309 @@ set -eu | |||
10 | : ${ALPINE_KEYS_SHA256:="f7832b848cedca482b145011cf516e82392f02a10713875cb09f39c7221c6f17"} | 10 | : ${ALPINE_KEYS_SHA256:="f7832b848cedca482b145011cf516e82392f02a10713875cb09f39c7221c6f17"} |
11 | 11 | ||
12 | die() { | 12 | die() { |
13 | printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2 # bold red | 13 | printf '\033[1;31mERROR:\033[0m %s\n' "$@" >&2 # bold red |
14 | exit 1 | 14 | exit 1 |
15 | } | 15 | } |
16 | 16 | ||
17 | einfo() { | 17 | einfo() { |
18 | printf '\n\033[1;36m> %s\033[0m\n' "$@" >&2 # bold cyan | 18 | printf '\n\033[1;36m> %s\033[0m\n' "$@" >&2 # bold cyan |
19 | } | 19 | } |
20 | 20 | ||
21 | rc_add() { | 21 | rc_add() { |
22 | local target="$1"; shift # target directory | 22 | local target="$1"; shift # target directory |
23 | local runlevel="$1"; shift # runlevel name | 23 | local runlevel="$1"; shift # runlevel name |
24 | local services="$*" # names of services | 24 | local services="$*" # names of services |
25 | 25 | ||
26 | local svc; for svc in $services; do | 26 | local svc; for svc in $services; do |
27 | mkdir -p "$target"/etc/runlevels/$runlevel | 27 | mkdir -p "$target"/etc/runlevels/$runlevel |
28 | ln -s /etc/init.d/$svc "$target"/etc/runlevels/$runlevel/$svc | 28 | ln -s /etc/init.d/$svc "$target"/etc/runlevels/$runlevel/$svc |
29 | echo " * service $svc added to runlevel $runlevel" | 29 | echo " * service $svc added to runlevel $runlevel" |
30 | done | 30 | done |
31 | } | 31 | } |
32 | 32 | ||
33 | wgets() ( | 33 | wgets() ( |
34 | local url="$1" # url to fetch | 34 | local url="$1" # url to fetch |
35 | local sha256="$2" # expected SHA256 sum of output | 35 | local sha256="$2" # expected SHA256 sum of output |
36 | local dest="$3" # output path and filename | 36 | local dest="$3" # output path and filename |
37 | 37 | ||
38 | wget -T 10 -q -O "$dest" "$url" | 38 | wget -T 10 -q -O "$dest" "$url" |
39 | echo "$sha256 $dest" | sha256sum -c > /dev/null | 39 | echo "$sha256 $dest" | sha256sum -c > /dev/null |
40 | ) | 40 | ) |
41 | 41 | ||
42 | 42 | ||
43 | validate_block_device() { | 43 | validate_block_device() { |
44 | local dev="$1" # target directory | 44 | local dev="$1" # target directory |
45 | 45 | ||
46 | lsblk -P --fs "$dev" >/dev/null 2>&1 || \ | 46 | lsblk -P --fs "$dev" >/dev/null 2>&1 || \ |
47 | die "'$dev' is not a valid block device" | 47 | die "'$dev' is not a valid block device" |
48 | 48 | ||
49 | if lsblk -P --fs "$dev" | grep -vq 'FSTYPE=""'; then | 49 | if lsblk -P --fs "$dev" | grep -vq 'FSTYPE=""'; then |
50 | die "Block device '$dev' is not blank" | 50 | die "Block device '$dev' is not blank" |
51 | fi | 51 | fi |
52 | } | 52 | } |
53 | 53 | ||
54 | fetch_apk_tools() { | 54 | fetch_apk_tools() { |
55 | local store="$(mktemp -d)" | 55 | local store="$(mktemp -d)" |
56 | local tarball="$(basename $APK_TOOLS_URI)" | 56 | local tarball="$(basename $APK_TOOLS_URI)" |
57 | 57 | ||
58 | wgets "$APK_TOOLS_URI" "$APK_TOOLS_SHA256" "$store/$tarball" | 58 | wgets "$APK_TOOLS_URI" "$APK_TOOLS_SHA256" "$store/$tarball" |
59 | tar -C "$store" -xf "$store/$tarball" | 59 | tar -C "$store" -xf "$store/$tarball" |
60 | 60 | ||
61 | find "$store" -name apk | 61 | find "$store" -name apk |
62 | } | 62 | } |
63 | 63 | ||
64 | make_filesystem() { | 64 | make_filesystem() { |
65 | local device="$1" # target device path | 65 | local device="$1" # target device path |
66 | local target="$2" # mount target | 66 | local target="$2" # mount target |
67 | 67 | ||
68 | mkfs.ext4 "$device" | 68 | mkfs.ext4 "$device" |
69 | e2label "$device" / | 69 | e2label "$device" / |
70 | mount "$device" "$target" | 70 | mount "$device" "$target" |
71 | } | 71 | } |
72 | 72 | ||
73 | setup_repositories() { | 73 | setup_repositories() { |
74 | local target="$1" # target directory | 74 | local target="$1" # target directory |
75 | local add_repos="$2" # extra repo lines, comma separated | 75 | local add_repos="$2" # extra repo lines, comma separated |
76 | 76 | ||
77 | mkdir -p "$target"/etc/apk/keys | 77 | mkdir -p "$target"/etc/apk/keys |
78 | cat > "$target"/etc/apk/repositories <<-EOF | 78 | cat > "$target"/etc/apk/repositories <<EOF |
79 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/main | 79 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/main |
80 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/community | 80 | http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_RELEASE/community |
81 | EOF | 81 | @edge-main http://dl-cdn.alpinelinux.org/alpine/edge/main |
82 | echo "$add_repos" | tr , "\012" >> "$target"/etc/apk/repositories | 82 | @edge-community http://dl-cdn.alpinelinux.org/alpine/edge/community |
83 | @edge-testing http://dl-cdn.alpinelinux.org/alpine/edge/testing | ||
84 | EOF | ||
85 | echo "$add_repos" | tr , "\012" >> "$target"/etc/apk/repositories | ||
83 | } | 86 | } |
84 | 87 | ||
85 | fetch_keys() { | 88 | fetch_keys() { |
86 | local target="$1" | 89 | local target="$1" |
87 | local tmp="$(mktemp -d)" | 90 | local tmp="$(mktemp -d)" |
88 | 91 | ||
89 | wgets "$ALPINE_KEYS" "$ALPINE_KEYS_SHA256" "$tmp/alpine-keys.apk" | 92 | wgets "$ALPINE_KEYS" "$ALPINE_KEYS_SHA256" "$tmp/alpine-keys.apk" |
90 | tar -C "$target" -xvf "$tmp"/alpine-keys.apk etc/apk/keys | 93 | tar -C "$target" -xvf "$tmp"/alpine-keys.apk etc/apk/keys |
91 | rm -rf "$tmp" | 94 | rm -rf "$tmp" |
92 | } | 95 | } |
93 | 96 | ||
94 | setup_chroot() { | 97 | setup_chroot() { |
95 | local target="$1" | 98 | local target="$1" |
96 | 99 | ||
97 | mount -t proc none "$target"/proc | 100 | mount -t proc none "$target"/proc |
98 | mount --bind /dev "$target"/dev | 101 | mount --bind /dev "$target"/dev |
99 | mount --bind /sys "$target"/sys | 102 | mount --bind /sys "$target"/sys |
100 | 103 | ||
101 | # Don't want to ship this but it's needed for bootstrap. Will be removed in | 104 | # Don't want to ship this but it's needed for bootstrap. Will be removed in |
102 | # the cleanup stage. | 105 | # the cleanup stage. |
103 | install -Dm644 /etc/resolv.conf "$target"/etc/resolv.conf | 106 | install -Dm644 /etc/resolv.conf "$target"/etc/resolv.conf |
104 | } | 107 | } |
105 | 108 | ||
106 | install_core_packages() { | 109 | install_core_packages() { |
107 | local target="$1" # target directory | 110 | local target="$1" # target directory |
108 | local flavor="$2" # kernel flavor | 111 | local flavor="$2" # kernel flavor |
109 | local add_pkgs="$3" # extra packages, space separated | 112 | local add_pkgs="$3" # extra packages, space separated |
110 | 113 | ||
111 | # Most from: https://git.alpinelinux.org/cgit/alpine-iso/tree/alpine-virt.packages | 114 | # Most from: https://git.alpinelinux.org/cgit/alpine-iso/tree/alpine-virt.packages |
112 | # | 115 | # |
113 | # linux-$flavor - linux kernel flavor to install | 116 | # sudo - to allow alpine user to become root, disallow root SSH logins |
114 | # e2fsprogs - required by init scripts to maintain ext4 volumes | 117 | # tiny-ec2-bootstrap - to bootstrap system from EC2 metadata |
115 | # mkinitfs - required to build custom initfs | 118 | # |
116 | # sudo - to allow alpine user to become root, disallow root SSH logins | 119 | chroot "$target" apk --no-cache add \ |
117 | # tiny-ec2-bootstrap - to bootstrap system from EC2 metadata | 120 | linux-"$flavor" \ |
118 | chroot "$target" apk --no-cache add \ | 121 | alpine-mirrors \ |
119 | linux-"$flavor" \ | 122 | chrony \ |
120 | alpine-mirrors \ | 123 | e2fsprogs \ |
121 | chrony \ | 124 | openssh \ |
122 | e2fsprogs \ | 125 | sudo \ |
123 | mkinitfs \ | 126 | tiny-ec2-bootstrap \ |
124 | openssh \ | 127 | tzdata \ |
125 | sudo \ | 128 | $add_pkgs |
126 | tiny-ec2-bootstrap \ | 129 | |
127 | tzdata \ | 130 | chroot "$target" apk --no-cache add --no-scripts syslinux |
128 | $add_pkgs | 131 | |
129 | 132 | # Disable starting getty for physical ttys because they're all inaccessible | |
130 | chroot "$target" apk --no-cache add --no-scripts syslinux | 133 | # anyhow. With this configuration boot messages will still display in the |
131 | 134 | # EC2 console. | |
132 | # Disable starting getty for physical ttys because they're all inaccessible | 135 | sed -Ei '/^tty[0-9]/s/^/#/' \ |
133 | # anyhow. With this configuration boot messages will still display in the | 136 | "$target"/etc/inittab |
134 | # EC2 console. | 137 | |
135 | sed -Ei '/^tty\d/s/^/#/' "$target"/etc/inittab | 138 | # Make it a little more obvious who is logged in by adding username to the |
136 | 139 | # prompt | |
137 | # Make it a little more obvious who is logged in by adding username to the | 140 | sed -i "s/^export PS1='/&\\\\u@/" "$target"/etc/profile |
138 | # prompt | ||
139 | sed -i "s/^export PS1='/&\\\\u@/" "$target"/etc/profile | ||
140 | } | 141 | } |
141 | 142 | ||
142 | create_initfs() { | 143 | create_initfs() { |
143 | local target="$1" | 144 | local target="$1" |
144 | 145 | ||
145 | # Create ENA feature for mkinitfs | 146 | # Create ENA feature for mkinitfs |
146 | echo "kernel/drivers/net/ethernet/amazon" > \ | 147 | echo "kernel/drivers/net/ethernet/amazon" > \ |
147 | "$target"/etc/mkinitfs/features.d/ena.modules | 148 | "$target"/etc/mkinitfs/features.d/ena.modules |
148 | 149 | ||
149 | # Enable ENA and NVME features these don't hurt for any instance and are | 150 | # Enable ENA and NVME features these don't hurt for any instance and are |
150 | # hard requirements of the 5 series and i3 series of instances | 151 | # hard requirements of the 5 series and i3 series of instances |
151 | sed -Ei 's/^features="([^"]+)"/features="\1 nvme ena"/' \ | 152 | sed -Ei 's/^features="([^"]+)"/features="\1 nvme ena"/' \ |
152 | "$target"/etc/mkinitfs/mkinitfs.conf | 153 | "$target"/etc/mkinitfs/mkinitfs.conf |
153 | 154 | ||
154 | chroot "$target" /sbin/mkinitfs $(basename $(find "$target"/lib/modules/* -maxdepth 0)) | 155 | chroot "$target" /sbin/mkinitfs $(basename $(find "$target"/lib/modules/* -maxdepth 0)) |
155 | } | 156 | } |
156 | 157 | ||
157 | setup_extlinux() { | 158 | setup_extlinux() { |
158 | local target="$1" | 159 | local target="$1" |
159 | 160 | ||
160 | # Must use disk labels instead of UUID or devices paths so that this works | 161 | # Must use disk labels instead of UUID or devices paths so that this works |
161 | # across instance familes. UUID works for many instances but breaks on the | 162 | # across instance familes. UUID works for many instances but breaks on the |
162 | # NVME ones because EBS volumes are hidden behind NVME devices. | 163 | # NVME ones because EBS volumes are hidden behind NVME devices. |
163 | # | 164 | # |
164 | # Enable ext4 because the root device is formatted ext4 | 165 | # Enable ext4 because the root device is formatted ext4 |
165 | # | 166 | # |
166 | # Shorten timeout because EC2 has no way to interact with instance console | 167 | # Shorten timeout because EC2 has no way to interact with instance console |
167 | # | 168 | # |
168 | # ttyS0 is the target for EC2s "Get System Log" feature whereas tty0 is the | 169 | # ttyS0 is the target for EC2s "Get System Log" feature whereas tty0 is the |
169 | # target for EC2s "Get Instance Screenshot" feature. Enabling the serial | 170 | # target for EC2s "Get Instance Screenshot" feature. Enabling the serial |
170 | # port early in extlinux gives the most complete output in the system log. | 171 | # port early in extlinux gives the most complete output in the system log. |
171 | sed -Ei -e "s|^[# ]*(root)=.*|\1=LABEL=/|" \ | 172 | sed -Ei -e "s|^[# ]*(root)=.*|\1=LABEL=/|" \ |
172 | -e "s|^[# ]*(default_kernel_opts)=.*|\1=\"console=ttyS0 console=tty0\"|" \ | 173 | -e "s|^[# ]*(default_kernel_opts)=.*|\1=\"console=ttyS0 console=tty0\"|" \ |
173 | -e "s|^[# ]*(serial_port)=.*|\1=ttyS0|" \ | 174 | -e "s|^[# ]*(serial_port)=.*|\1=ttyS0|" \ |
174 | -e "s|^[# ]*(modules)=.*|\1=sd-mod,usb-storage,ext4|" \ | 175 | -e "s|^[# ]*(modules)=.*|\1=sd-mod,usb-storage,ext4|" \ |
175 | -e "s|^[# ]*(default)=.*|\1=hardened|" \ | 176 | -e "s|^[# ]*(default)=.*|\1=hardened|" \ |
176 | -e "s|^[# ]*(timeout)=.*|\1=1|" \ | 177 | -e "s|^[# ]*(timeout)=.*|\1=1|" \ |
177 | "$target"/etc/update-extlinux.conf | 178 | "$target"/etc/update-extlinux.conf |
178 | } | 179 | } |
179 | 180 | ||
180 | install_extlinux() { | 181 | install_extlinux() { |
181 | local target="$1" | 182 | local target="$1" |
182 | 183 | ||
183 | chroot "$target" /sbin/extlinux --install /boot | 184 | chroot "$target" /sbin/extlinux --install /boot |
184 | chroot "$target" /sbin/update-extlinux --warn-only | 185 | chroot "$target" /sbin/update-extlinux --warn-only |
185 | } | 186 | } |
186 | 187 | ||
187 | setup_fstab() { | 188 | setup_fstab() { |
188 | local target="$1" | 189 | local target="$1" |
189 | 190 | ||
190 | cat > "$target"/etc/fstab <<-EOF | 191 | cat > "$target"/etc/fstab <<EOF |
191 | # <fs> <mountpoint> <type> <opts> <dump/pass> | 192 | # <fs> <mountpoint> <type> <opts> <dump/pass> |
192 | LABEL=/ / ext4 defaults,noatime 1 1 | 193 | LABEL=/ / ext4 defaults,noatime 1 1 |
193 | EOF | 194 | EOF |
194 | } | 195 | } |
195 | 196 | ||
196 | setup_networking() { | 197 | setup_networking() { |
197 | local target="$1" | 198 | local target="$1" |
198 | 199 | ||
199 | cat > "$target"/etc/network/interfaces <<-EOF | 200 | cat > "$target"/etc/network/interfaces <<EOF |
200 | auto lo | 201 | auto lo |
201 | iface lo inet loopback | 202 | iface lo inet loopback |
202 | 203 | ||
203 | auto eth0 | 204 | auto eth0 |
204 | iface eth0 inet dhcp | 205 | iface eth0 inet dhcp |
205 | EOF | 206 | EOF |
206 | } | 207 | } |
207 | 208 | ||
208 | enable_services() { | 209 | enable_services() { |
209 | local target="$1" | 210 | local target="$1" |
210 | 211 | ||
211 | rc_add "$target" default sshd chronyd networking tiny-ec2-bootstrap | 212 | rc_add "$target" default sshd chronyd networking tiny-ec2-bootstrap |
212 | rc_add "$target" sysinit devfs dmesg mdev hwdrivers | 213 | rc_add "$target" sysinit devfs dmesg mdev hwdrivers |
213 | rc_add "$target" boot modules hwclock swap hostname sysctl bootmisc syslog acpid | 214 | rc_add "$target" boot modules hwclock swap hostname sysctl bootmisc syslog acpid |
214 | rc_add "$target" shutdown killprocs savecache mount-ro | 215 | rc_add "$target" shutdown killprocs savecache mount-ro |
215 | } | 216 | } |
216 | 217 | ||
217 | create_alpine_user() { | 218 | create_alpine_user() { |
218 | local target="$1" | 219 | local target="$1" |
219 | 220 | ||
220 | # Allow members of the wheel group to sudo without a password. By default | 221 | # Allow members of the wheel group to sudo without a password. By default |
221 | # this will only be the alpine user. This allows us to ship an AMI that is | 222 | # this will only be the alpine user. This allows us to ship an AMI that is |
222 | # accessible via SSH using the user's configured SSH keys (thanks to | 223 | # accessible via SSH using the user's configured SSH keys (thanks to |
223 | # tiny-ec2-bootstrap) but does not allow remote root access which is the | 224 | # tiny-ec2-bootstrap) but does not allow remote root access which is the |
224 | # best-practice. | 225 | # best-practice. |
225 | sed -i '/%wheel .* NOPASSWD: .*/s/^# //' "$target"/etc/sudoers | 226 | sed -i '/%wheel .* NOPASSWD: .*/s/^# //' "$target"/etc/sudoers |
226 | 227 | ||
227 | # There is no real standard ec2 username across AMIs, Amazon uses ec2-user | 228 | # There is no real standard ec2 username across AMIs, Amazon uses ec2-user |
228 | # for their Amazon Linux AMIs but Ubuntu uses ubuntu, Fedora uses fedora, | 229 | # for their Amazon Linux AMIs but Ubuntu uses ubuntu, Fedora uses fedora, |
229 | # etc... (see: https://alestic.com/2014/01/ec2-ssh-username/). So our user | 230 | # etc... (see: https://alestic.com/2014/01/ec2-ssh-username/). So our user |
230 | # and group are alpine because this is Alpine Linux. On instance bootstrap | 231 | # and group are alpine because this is Alpine Linux. On instance bootstrap |
231 | # the user can create whatever users they want and delete this one. | 232 | # the user can create whatever users they want and delete this one. |
232 | chroot "$target" /usr/sbin/addgroup alpine | 233 | chroot "$target" /usr/sbin/addgroup alpine |
233 | chroot "$target" /usr/sbin/adduser -h /home/alpine -s /bin/sh -G alpine -D alpine | 234 | chroot "$target" /usr/sbin/adduser -h /home/alpine -s /bin/sh -G alpine -D alpine |
234 | chroot "$target" /usr/sbin/addgroup alpine wheel | 235 | chroot "$target" /usr/sbin/addgroup alpine wheel |
235 | chroot "$target" /usr/bin/passwd -u alpine | 236 | chroot "$target" /usr/bin/passwd -u alpine |
236 | } | 237 | } |
237 | 238 | ||
238 | configure_ntp() { | 239 | configure_ntp() { |
239 | local target="$1" | 240 | local target="$1" |
240 | 241 | ||
241 | # EC2 provides an instance-local NTP service syncronized with GPS and | 242 | # EC2 provides an instance-local NTP service syncronized with GPS and |
242 | # atomic clocks in-region. Prefer this over external NTP hosts when running | 243 | # atomic clocks in-region. Prefer this over external NTP hosts when running |
243 | # in EC2. | 244 | # in EC2. |
244 | # | 245 | # |
245 | # See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html | 246 | # See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html |
246 | sed -i 's/^server .*/server 169.254.169.123/' "$target"/etc/chrony/chrony.conf | 247 | sed -i 's/^pool .*/server 169.254.169.123 iburst/' "$target"/etc/chrony/chrony.conf |
247 | } | 248 | } |
248 | 249 | ||
249 | cleanup() { | 250 | cleanup() { |
250 | local target="$1" | 251 | local target="$1" |
251 | 252 | ||
252 | # Sweep cruft out of the image that doesn't need to ship or will be | 253 | # Sweep cruft out of the image that doesn't need to ship or will be |
253 | # re-generated when the image boots | 254 | # re-generated when the image boots |
254 | rm -f \ | 255 | rm -f \ |
255 | "$target"/var/cache/apk/* \ | 256 | "$target"/var/cache/apk/* \ |
256 | "$target"/etc/resolv.conf \ | 257 | "$target"/etc/resolv.conf \ |
257 | "$target"/root/.ash_history \ | 258 | "$target"/root/.ash_history \ |
258 | "$target"/etc/*- | 259 | "$target"/etc/*- |
259 | 260 | ||
260 | umount \ | 261 | umount \ |
261 | "$target"/dev \ | 262 | "$target"/dev \ |
262 | "$target"/proc \ | 263 | "$target"/proc \ |
263 | "$target"/sys | 264 | "$target"/sys |
264 | 265 | ||
265 | umount "$target" | 266 | umount "$target" |
266 | } | 267 | } |
267 | 268 | ||
268 | main() { | 269 | main() { |
269 | [ "$#" -ne 4 ] && { echo "usage: $0 <block-device> <kernel-flavor> '<repo>[,<repo>]' '<pkg>[ <pkg>]'"; exit 1; } | 270 | [ "$#" -ne 3 ] && { echo "usage: $0 <kernel-flavor> '<repo>[,<repo>]' '<pkg>[ <pkg>]'"; exit 1; } |
270 | 271 | ||
271 | device="$1" | 272 | local flavor="$1" |
272 | flavor="$2" | 273 | local add_repos="$2" |
273 | add_repos="$3" | 274 | local add_pkgs="$3" |
274 | add_pkgs="$4" | ||
275 | 275 | ||
276 | target="/mnt/target" | 276 | local device="/dev/xvdf" |
277 | local target="/mnt/target" | ||
277 | 278 | ||
278 | validate_block_device "$device" | 279 | validate_block_device "$device" |
279 | 280 | ||
280 | [ -d "$target" ] || mkdir "$target" | 281 | [ -d "$target" ] || mkdir "$target" |
281 | 282 | ||
282 | einfo "Fetching static APK tools" | 283 | einfo "Fetching static APK tools" |
283 | apk="$(fetch_apk_tools)" | 284 | apk="$(fetch_apk_tools)" |
284 | 285 | ||
285 | einfo "Creating root filesystem" | 286 | einfo "Creating root filesystem" |
286 | make_filesystem "$device" "$target" | 287 | make_filesystem "$device" "$target" |
287 | 288 | ||
288 | setup_repositories "$target" "$add_repos" | 289 | setup_repositories "$target" "$add_repos" |
289 | 290 | ||
290 | einfo "Fetching Alpine signing keys" | 291 | einfo "Fetching Alpine signing keys" |
291 | fetch_keys "$target" | 292 | fetch_keys "$target" |
292 | 293 | ||
293 | einfo "Installing base system" | 294 | einfo "Installing base system" |
294 | $apk add --root "$target" --update-cache --initdb alpine-base | 295 | $apk add --root "$target" --update-cache --initdb alpine-base |
295 | 296 | ||
296 | setup_chroot "$target" | 297 | setup_chroot "$target" |
297 | 298 | ||
298 | einfo "Installing core packages" | 299 | einfo "Installing core packages" |
299 | install_core_packages "$target" "$flavor" "$add_pkgs" | 300 | install_core_packages "$target" "$flavor" "$add_pkgs" |
300 | 301 | ||
301 | einfo "Configuring and enabling boot loader" | 302 | einfo "Configuring and enabling boot loader" |
302 | create_initfs "$target" | 303 | create_initfs "$target" |
303 | setup_extlinux "$target" | 304 | setup_extlinux "$target" |
304 | install_extlinux "$target" | 305 | install_extlinux "$target" |
305 | 306 | ||
306 | einfo "Configuring system" | 307 | einfo "Configuring system" |
307 | setup_fstab "$target" | 308 | setup_fstab "$target" |
308 | setup_networking "$target" | 309 | setup_networking "$target" |
309 | enable_services "$target" | 310 | enable_services "$target" |
310 | create_alpine_user "$target" | 311 | create_alpine_user "$target" |
311 | configure_ntp "$target" | 312 | configure_ntp "$target" |
312 | 313 | ||
313 | einfo "All done, cleaning up" | 314 | einfo "All done, cleaning up" |
314 | cleanup "$target" | 315 | cleanup "$target" |
315 | } | 316 | } |
316 | 317 | ||
317 | main "$@" | 318 | main "$@" |
diff --git a/variables.json-default b/variables.json-default index c82e111..d45378f 100644 --- a/variables.json-default +++ b/variables.json-default | |||
@@ -5,18 +5,16 @@ | |||
5 | "ami_desc_prefix": "Alpine Linux ", | 5 | "ami_desc_prefix": "Alpine Linux ", |
6 | "ami_desc_suffix": " Release with EC2 Optimizations", | 6 | "ami_desc_suffix": " Release with EC2 Optimizations", |
7 | "kernel_flavor": "vanilla@edge-main", | 7 | "kernel_flavor": "vanilla@edge-main", |
8 | "add_repos": "@edge-main http://dl-cdn.alpinelinux.org/alpine/edge/main,@edge-testing http://dl-cdn.alpinelinux.org/alpine/edge/testing", | 8 | "add_repos": "", |
9 | "add_pkgs": "acct aws-ena-driver-vanilla@edge-testing e2fsprogs-extra", | 9 | "add_pkgs": "aws-ena-driver-vanilla@edge-testing", |
10 | "ena_enable": "true", | 10 | "ena_enable": "true", |
11 | "sriov_enable": "false", | ||
12 | "volume_size": "1", | 11 | "volume_size": "1", |
12 | "encrypt_ami": "false", | ||
13 | "ami_access": "all", | 13 | "ami_access": "all", |
14 | "deploy_regions": "us-east-1,us-east-2,us-west-1,us-west-2,ca-central-1,eu-central-1,eu-west-1,eu-west-2,eu-west-3,ap-northeast-1,ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-south-1,sa-east-1", | 14 | "deploy_regions": "us-east-1,us-east-2,us-west-1,us-west-2,ca-central-1,eu-central-1,eu-west-1,eu-west-2,eu-west-3,ap-northeast-1,ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-south-1,sa-east-1", |
15 | 15 | ||
16 | "vpc": "", | 16 | "vpc": "", |
17 | "subnet": "", | 17 | "subnet": "", |
18 | "security_group": "", | 18 | "security_group": "", |
19 | "public_ip": "false", | 19 | "public_ip": "false" |
20 | "build_instance_type": "t2.nano", | ||
21 | "volume_name": "/dev/xvdf" | ||
22 | } | 20 | } |
diff --git a/variables.json-example b/variables.json-example index c422de5..0e10c9a 100644 --- a/variables.json-example +++ b/variables.json-example | |||
@@ -1,4 +1,5 @@ | |||
1 | # NOTE: This is file not valid JSON. | 1 | # *** NOTE: This is file not valid JSON! *** |
2 | |||
2 | { | 3 | { |
3 | ### Build Options ### | 4 | ### Build Options ### |
4 | 5 | ||
@@ -13,31 +14,37 @@ | |||
13 | "ami_desc_prefix": "Alpine Linux ", | 14 | "ami_desc_prefix": "Alpine Linux ", |
14 | "ami_desc_suffix": " Release with EC2 Optimizations", | 15 | "ami_desc_suffix": " Release with EC2 Optimizations", |
15 | 16 | ||
16 | # Kernel "flavor" to install. 'virt' is a slim choice, but doesn't currently | 17 | # Kernel "flavor" to install. |
17 | # include NVME support and there is no matching 'aws-ena-driver' package. | 18 | # |
19 | # 'virt' is the slim choice, but doesn't currently include NVMe support and | ||
20 | # there is no matching 'aws-ena-driver' package. When these features are | ||
21 | # available, this kernel flavor will be the default (if not hardcoded). | ||
22 | # | ||
18 | # 'vanilla' installs a lot of unneeded stuff (for an AMI), but does support | 23 | # 'vanilla' installs a lot of unneeded stuff (for an AMI), but does support |
19 | # NVME; however, there is no matching ENA driver in the main repo. In order | 24 | # NVMe; however, there is no matching ENA driver in the main repo. In order |
20 | # to support NVME and ENA, we need to use 'vanilla@edge-main', which matches | 25 | # to support NVMe and ENA, we need to use 'vanilla@edge-main', which matches |
21 | # the 'aws-ena-driver@edge-testing' package. | 26 | # the 'aws-ena-driver@edge-testing' package. |
27 | # | ||
22 | "kernel_flavor": "vanilla@edge-main", | 28 | "kernel_flavor": "vanilla@edge-main", |
23 | 29 | ||
24 | # Comma separated list of lines to add to /etc/apk/repositories. We need | 30 | # Comma separated list of custom lines to add to /etc/apk/repositories. |
25 | # edge/main and edge/testing for simultaneous NVME and ENA support. | 31 | # @edge-main, @edge-community, and @edge-testing repos have been predefined. |
26 | "add_repos": "@edge-main http://dl-cdn.alpinelinux.org/alpine/edge/main,@edge-testing http://dl-cdn.alpinelinux.org/alpine/edge/testing", | 32 | "add_repos": "", |
27 | 33 | ||
28 | # Space separated list of additional packages to add to the AMI. | 34 | # Space separated list of additional packages to add to the AMI. |
29 | # acct - system accounting utilities (sa, etc.) | 35 | # aws-ena-driver-vanilla - ENA driver (until we have a 'virt' flavor) |
30 | # aws-ena-driver-vanilla - Enhanced Network Adapter kernel module | 36 | "add_pkgs": "aws-ena-driver-vanilla@edge-testing", |
31 | # e2fsprogs-extra - ec2-tiny-bootstrap's currently undeclared dependency (resize2fs) | ||
32 | "add_pkgs": "acct aws-ena-driver-vanilla@edge-testing e2fsprogs-extra", | ||
33 | 37 | ||
34 | # Enable ENA/SRIOV support on the AMI. | 38 | # Enable ENA support on the AMI. |
39 | # When ENA is available for the 'virt' kernel, this will always be on. | ||
35 | "ena_enable": "true", | 40 | "ena_enable": "true", |
36 | "sriov_enable": "false", | ||
37 | 41 | ||
38 | # Size of the AMI image (in GiB). | 42 | # Size of the AMI image (in GiB). |
39 | "volume_size": "1", | 43 | "volume_size": "1", |
40 | 44 | ||
45 | # Encrypt the AMI? | ||
46 | "encrypt_ami": "false", | ||
47 | |||
41 | # Comma separated list of groups that should have access to the AMI. However, | 48 | # Comma separated list of groups that should have access to the AMI. However, |
42 | # only two values are currently supported: 'all' for public, '' for private. | 49 | # only two values are currently supported: 'all' for public, '' for private. |
43 | "ami_access": "all", | 50 | "ami_access": "all", |
@@ -62,12 +69,6 @@ | |||
62 | # Assign a public IP to the builder instance. Set to 'true' for if you need | 69 | # Assign a public IP to the builder instance. Set to 'true' for if you need |
63 | # to initiate the build from somewhere that wouldn't normally be able to | 70 | # to initiate the build from somewhere that wouldn't normally be able to |
64 | # access the builder instance's private network. | 71 | # access the builder instance's private network. |
65 | "public_ip": "false", | 72 | "public_ip": "false" |
66 | |||
67 | # Instance type to use for building. | ||
68 | "build_instance_type": "t2.nano", | ||
69 | 73 | ||
70 | # Don't override this without a good reason, and if you do just make sure it | ||
71 | # gets passed all the way through to the make_ami script. | ||
72 | "volume_name": "/dev/xvdf" | ||
73 | } | 74 | } |