deploy.sh 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/bin/sh
  2. set -xeu
  3. chroot_dir="/mnt"
  4. arch="$(uname -m)"
  5. mirror="https://dl-cdn.alpinelinux.org/alpine"
  6. alpine_version="v3.20"
  7. apk_version="2.14.4-r1"
  8. apk_link="${mirror}/${alpine_version}/main/${arch}/apk-tools-static-${apk_version}.apk"
  9. fatal() {
  10. echo "$1" >&2
  11. }
  12. die() {
  13. fatal "$2"
  14. exit "$1"
  15. }
  16. usage() {
  17. cat >&2 <<EOF
  18. Usage: $0 <action> [-r <chroot_dir>] [-h]
  19. Action:
  20. rootfs deploy alpine rootfs in <chroot_dir>
  21. setup setup rootfs inside chroot environment
  22. full deploy rootfs and run setup inside chroot
  23. Options:
  24. -r <chroot_dir> specify chroot directory
  25. [default: $chroot_dir]
  26. -h show this help message
  27. EOF
  28. exit "$1"
  29. }
  30. ensure_root() {
  31. if [ "$(id -u)" = 0 ]; then
  32. return 0
  33. fi
  34. die 1 "The script must be run as root"
  35. }
  36. parse_action() {
  37. case "$1" in
  38. rootfs|setup|full) action="$1" ;;
  39. *)
  40. fatal "unknown op: $1"
  41. usage 1
  42. ;;
  43. esac
  44. }
  45. parse_args() {
  46. [ "$#" -lt 1 ] && usage 1
  47. parse_action "$1" && shift
  48. while getopts "r:h" opt; do
  49. case "$opt" in
  50. r) chroot_dir="$(realpath "$OPTARG")" ;;
  51. h) usage 0 ;;
  52. ?) usage 1 ;;
  53. esac
  54. done
  55. }
  56. # download static apk and print the executable filename
  57. download_apk() {
  58. local tmpdir
  59. tmpdir="$(mktemp -d)"
  60. curl -L "$apk_link" | tar -C "$tmpdir" -xzf -
  61. echo "$tmpdir/sbin/apk.static"
  62. }
  63. install_rootfs() {
  64. apk_bin="$(download_apk)"
  65. mkdir -p "$chroot_dir/etc/apk"
  66. "$apk_bin" -X "$mirror/$alpine_version/main" -U --allow-untrusted \
  67. -p "$chroot_dir" --initdb add alpine-base e2fsprogs
  68. cat > "$chroot_dir/etc/resolv.conf" <<EOF
  69. nameserver 1.1.1.1
  70. nameserver 1.0.0.1
  71. nameserver 8.8.8.8
  72. EOF
  73. cat > "$chroot_dir/etc/apk/repositories" <<EOF
  74. $mirror/$alpine_version/main
  75. $mirror/$alpine_version/community
  76. EOF
  77. }
  78. bind_mount_vfs() {
  79. for fs in proc sys dev; do
  80. mount -o bind "/$fs" "$chroot_dir/$fs"
  81. done
  82. }
  83. install_inside_chroot() {
  84. deploy_name="$(realpath "$0")"
  85. inside_deploy_path="$chroot_dir/deploy.sh"
  86. cp "$deploy_name" "$inside_deploy_path"
  87. chmod +x "$inside_deploy_path"
  88. bind_mount_vfs
  89. chroot "$chroot_dir" /deploy.sh setup
  90. }
  91. extra_setup_vim() {
  92. apk add vim
  93. ln -s "$(which vim)" /usr/local/bin/vi
  94. }
  95. extra_setup_fish() {
  96. apk add fish
  97. # chsh -s "$(which fish)"
  98. }
  99. setup_extra() {
  100. extra_setup_vim
  101. extra_setup_fish
  102. }
  103. install_kernel() {
  104. apk add linux-virt linux-firmware-none
  105. }
  106. install_packages() {
  107. apk add chrony acpi busybox-mdev-openrc
  108. apk add grub grub-bios
  109. apk add openssh blkid
  110. install_kernel
  111. }
  112. part_uuid() {
  113. blkid -o export -s UUID "$1" | sed -n -e 2p
  114. }
  115. mountpoint_dev() {
  116. mount | grep "on $1 " | awk '{ print $1 }'
  117. }
  118. mountpoint_fstype() {
  119. mount | grep "on $1 " | awk '{ print $5 }'
  120. }
  121. mountpoint_options() {
  122. mount | grep "on $1 " | awk '{ print $6 }' | tr -d '()'
  123. }
  124. # $1: mountpoint
  125. # $2: check on boot
  126. gen_fstab_entry() {
  127. _mnt="$1"
  128. _dev="$(mountpoint_dev "$_mnt")"
  129. _fs="$(mountpoint_fstype "$_mnt")"
  130. _options="$(mountpoint_options "$_mnt")"
  131. _uuid="$(part_uuid "$_dev")"
  132. case "$2" in
  133. y|Y|1) _check=1 ;;
  134. *) _check=0 ;;
  135. esac
  136. echo "$_uuid $_mnt $_fs $_options 0 $_check"
  137. }
  138. gen_fstab() {
  139. gen_fstab_entry / y
  140. echo "tmpfs /tmp tmpfs nosuid,nodev 0 0"
  141. }
  142. populate_fstab() {
  143. gen_fstab | tee /etc/fstab
  144. }
  145. enable_services() {
  146. rc-update add devfs sysinit
  147. rc-update add dmesg sysinit
  148. rc-update add mdev sysinit
  149. rc-update add hwclock boot
  150. rc-update add modules boot
  151. rc-update add sysctl boot
  152. rc-update add hostname boot
  153. rc-update add bootmisc boot
  154. rc-update add syslog boot
  155. rc-update add localmount boot
  156. rc-update add networking boot
  157. rc-update add mount-ro shutdown
  158. rc-update add killprocs shutdown
  159. rc-update add savecache shutdown
  160. rc-update add acpid default
  161. rc-update add crond default
  162. rc-update add chronyd default
  163. rc-update add sshd default
  164. }
  165. setup_rootfs() {
  166. install_packages
  167. enable_services
  168. echo "Set root password:"
  169. passwd
  170. setup_extra
  171. }
  172. full_install() {
  173. install_rootfs
  174. install_inside_chroot
  175. }
  176. parse_args "$@"
  177. ensure_root
  178. case "$action" in
  179. rootfs) install_rootfs ;;
  180. setup) setup_rootfs ;;
  181. full) full_install ;;
  182. *) usage 1;;
  183. esac