K3s安装注意事项

2024-02-19
9分钟阅读时长

k3s自带了containerd作为CRI实现,不过我们一般习惯上还是使用docker作为运行时。但是两者结合使用有一些坑,这里做一下记录。

不用containerd的主要原因是它的命令太难记,ctr/ctictl做的什么jb玩意儿。还有一个原因是,如果只想用容器不想用k8s,docker的功能是最完善的。

如果你选择使用containerd,那么可以考虑安装nerdctl,它的命令格式与docker基本一致,具体使用方法可以参考这里

安装docker

需要注意的是,k3s并不是与docker的所有版本都兼容!一般来说,最好不要使用最新的版本,大版本号降低一级更好。比如这篇博客发布的时候,最新的docker是25.0.3,但是k3s只能兼容到24.0.9,因此安装docker的时候,就不能安装最新的版本,否则k3s无法正常启动!

安装脚本是根据官方脚本修改的,适配了常见的发行版,参考:

点击折叠
#!/bin/sh
set -e
SCRIPT_COMMIT_SHA="a8a6b338bdfedd7ddefb96fe3e7fe7d4036d945a"

CHANNEL="stable"
DOWNLOAD_URL="https://download.docker.com"
REPO_FILE="docker-ce.repo"
VERSION="24.0.9"
DIND_TEST_WAIT=${DIND_TEST_WAIT:-3s} # Wait time until docker start at dind test env

# Issue https://github.com/rancher/rancher/issues/29246
adjust_repo_releasever() {
	DOWNLOAD_URL="https://download.docker.com"
	case $1 in
	7*)
		releasever=7
		;;
	8*)
		releasever=8
		;;
	*)
		# fedora, or unsupported
		return
		;;
	esac

	for channel in "stable" "test" "nightly"; do
		$sh_c "$config_manager --setopt=docker-ce-${channel}.baseurl=${DOWNLOAD_URL}/linux/centos/${releasever}/\\\$basearch/${channel} --save"
		$sh_c "$config_manager --setopt=docker-ce-${channel}-debuginfo.baseurl=${DOWNLOAD_URL}/linux/centos/${releasever}/debug-\\\$basearch/${channel} --save"
		$sh_c "$config_manager --setopt=docker-ce-${channel}-source.baseurl=${DOWNLOAD_URL}/linux/centos/${releasever}/source/${channel} --save"
	done
}

mirror=''
DRY_RUN=${DRY_RUN:-}
while [ $# -gt 0 ]; do
	case "$1" in
	--mirror)
		mirror="$2"
		shift
		;;
	--dry-run)
		DRY_RUN=1
		;;
	--*)
		echo "Illegal option $1"
		;;
	esac
	shift $(($# > 0 ? 1 : 0))
done

case "$mirror" in
Aliyun)
	DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
	;;
AzureChinaCloud)
	DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
	;;
esac

start_docker() {
	if [ ! -z "$DIND_TEST" ]; then
		# Starting dockerd manually due to dind env is not using systemd
		dockerd &
		sleep "$DIND_TEST_WAIT"
	elif [ -d '/run/systemd/system' ]; then
		$sh_c 'systemctl start docker'
	else
		$sh_c 'service docker start'
	fi
}

command_exists() {
	command -v "$@" >/dev/null 2>&1
}

# version_gte checks if the version specified in $VERSION is at least
# the given CalVer (YY.MM) version. returns 0 (success) if $VERSION is either
# unset (=latest) or newer or equal than the specified version. Returns 1 (fail)
# otherwise.
#
# examples:
#
# VERSION=20.10
# version_gte 20.10 // 0 (success)
# version_gte 19.03 // 0 (success)
# version_gte 21.10 // 1 (fail)
version_gte() {
	if [ -z "$VERSION" ]; then
		return 0
	fi
	eval calver_compare "$VERSION" "$1"
}

# calver_compare compares two CalVer (YY.MM) version strings. returns 0 (success)
# if version A is newer or equal than version B, or 1 (fail) otherwise. Patch
# releases and pre-release (-alpha/-beta) are not taken into account
#
# examples:
#
# calver_compare 20.10 19.03 // 0 (success)
# calver_compare 20.10 20.10 // 0 (success)
# calver_compare 19.03 20.10 // 1 (fail)
calver_compare() (
	set +x

	yy_a="$(echo "$1" | cut -d'.' -f1)"
	yy_b="$(echo "$2" | cut -d'.' -f1)"
	if [ "$yy_a" -lt "$yy_b" ]; then
		return 1
	fi
	if [ "$yy_a" -gt "$yy_b" ]; then
		return 0
	fi
	mm_a="$(echo "$1" | cut -d'.' -f2)"
	mm_b="$(echo "$2" | cut -d'.' -f2)"
	if [ "${mm_a#0}" -lt "${mm_b#0}" ]; then
		return 1
	fi

	return 0
)

is_dry_run() {
	if [ -z "$DRY_RUN" ]; then
		return 1
	else
		return 0
	fi
}

is_wsl() {
	case "$(uname -r)" in
	*microsoft*) true ;; # WSL 2
	*Microsoft*) true ;; # WSL 1
	*) false ;;
	esac
}

is_darwin() {
	case "$(uname -s)" in
	*darwin*) true ;;
	*Darwin*) true ;;
	*) false ;;
	esac
}

deprecation_notice() {
	distro=$1
	distro_version=$2
	echo
	printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
	printf "    This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
	echo "    No updates or security fixes will be released for this distribution, and users are recommended"
	echo "    to upgrade to a currently maintained version of $distro."
	echo
	printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
	echo
	sleep 10
}

get_distribution() {
	lsb_dist=""
	# Every system that we officially support has /etc/os-release
	if [ -r /etc/os-release ]; then
		lsb_dist="$(. /etc/os-release && echo "$ID")"
	fi
	# Returning an empty string here should be alright since the
	# case statements don't act unless you provide an actual value
	echo "$lsb_dist"
}

echo_docker_as_nonroot() {
	if is_dry_run; then
		return
	fi
	if command_exists docker && [ -e /var/run/docker.sock ]; then
		(
			set -x
			$sh_c 'docker version'
		) || true
	fi

	# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
	echo
	echo "================================================================================"
	echo
	if version_gte "20.10"; then
		echo "To run Docker as a non-privileged user, consider setting up the"
		echo "Docker daemon in rootless mode for your user:"
		echo
		echo "    dockerd-rootless-setuptool.sh install"
		echo
		echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
		echo
	fi
	echo
	echo "To run the Docker daemon as a fully privileged service, but granting non-root"
	echo "users access, refer to https://docs.docker.com/go/daemon-access/"
	echo
	echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
	echo "         to root access on the host. Refer to the 'Docker daemon attack surface'"
	echo "         documentation for details: https://docs.docker.com/go/attack-surface/"
	echo
	echo "================================================================================"
	echo
}

# Check if this is a forked Linux distro
check_forked() {

	# Check for lsb_release command existence, it usually exists in forked distros
	if command_exists lsb_release; then
		# Check if the `-u` option is supported
		set +e
		lsb_release -a -u >/dev/null 2>&1
		lsb_release_exit_code=$?
		set -e

		# Check if the command has exited successfully, it means we're in a forked distro
		if [ "$lsb_release_exit_code" = "0" ]; then
			# Print info about current distro
			cat <<-EOF
				You're using '$lsb_dist' version '$dist_version'.
			EOF

			# Get the upstream release info
			lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
			dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')

			# Print info about upstream distro
			cat <<-EOF
				Upstream release is '$lsb_dist' version '$dist_version'.
			EOF
		else
			if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
				if [ "$lsb_dist" = "osmc" ]; then
					# OSMC runs Raspbian
					lsb_dist=raspbian
				else
					# We're Debian and don't even know it!
					lsb_dist=debian
				fi
				dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
				case "$dist_version" in
				12)
					dist_version="bookworm"
					;;
				11)
					dist_version="bullseye"
					;;
				10)
					dist_version="buster"
					;;
				9)
					dist_version="stretch"
					;;
				8)
					dist_version="jessie"
					;;
				esac
			fi
		fi
	fi
}

do_install() {
	echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"

	if command_exists docker; then
		cat >&2 <<-'EOF'
			Warning: the "docker" command appears to already exist on this system.

			If you already have Docker installed, this script can cause trouble, which is
			why we're displaying this warning and provide the opportunity to cancel the
			installation.

			If you installed the current Docker package using this script and are using it
			again to update Docker, you can safely ignore this message.

			You may press Ctrl+C now to abort this script.
		EOF
		(
			set -x
			sleep 20
		)
	fi

	user="$(id -un 2>/dev/null || true)"

	sh_c='sh -c'
	if [ "$user" != 'root' ]; then
		if command_exists sudo; then
			sh_c='sudo -E sh -c'
		elif command_exists su; then
			sh_c='su -c'
		else
			cat >&2 <<-'EOF'
				Error: this installer needs the ability to run commands as root.
				We are unable to find either "sudo" or "su" available to make this happen.
			EOF
			exit 1
		fi
	fi

	if is_dry_run; then
		sh_c="echo"
	fi

	# perform some very rudimentary platform detection
	lsb_dist=$(get_distribution)
	lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"

	if is_wsl; then
		echo
		echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
		echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
		echo
		cat >&2 <<-'EOF'

			You may press Ctrl+C now to abort this script.
		EOF
		(
			set -x
			sleep 20
		)
	fi

	case "$lsb_dist" in

	ubuntu)
		if command_exists lsb_release; then
			dist_version="$(lsb_release --codename | cut -f2)"
		fi
		if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
			dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
		fi
		;;

	debian | raspbian)
		dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
		case "$dist_version" in
		12)
			dist_version="bookworm"
			;;
		11)
			dist_version="bullseye"
			;;
		10)
			dist_version="buster"
			;;
		9)
			dist_version="stretch"
			;;
		8)
			dist_version="jessie"
			;;
		esac
		;;

	centos | rhel | sles | rocky)
		if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
			dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
		fi

		;;

	oracleserver | ol)
		lsb_dist="ol"
		# need to switch lsb_dist to match yum repo URL
		dist_version="$(rpm -q --whatprovides redhat-release --queryformat "%{VERSION}\n" | sed 's/\/.*//' | sed 's/\..*//' | sed 's/Server*//')"
		;;

	*)
		if command_exists lsb_release; then
			dist_version="$(lsb_release --release | cut -f2)"
		fi
		if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
			dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
		fi
		;;

	esac

	# Check if this is a forked Linux distro
	check_forked

	# Print deprecation warnings for distro versions that recently reached EOL,
	# but may still be commonly used (especially LTS versions).
	case "$lsb_dist.$dist_version" in
	debian.stretch | debian.jessie)
		deprecation_notice "$lsb_dist" "$dist_version"
		;;
	raspbian.stretch | raspbian.jessie)
		deprecation_notice "$lsb_dist" "$dist_version"
		;;
	ubuntu.xenial | ubuntu.trusty)
		deprecation_notice "$lsb_dist" "$dist_version"
		;;
	fedora.*)
		if [ "$dist_version" -lt 36 ]; then
			deprecation_notice "$lsb_dist" "$dist_version"
		fi
		;;
	esac

	# Run setup for each distro accordingly
	case "$lsb_dist" in
	ubuntu | debian | raspbian)
		pre_reqs="apt-transport-https ca-certificates curl"
		if ! command -v gpg >/dev/null; then
			pre_reqs="$pre_reqs gnupg"
		fi
		apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL"
		(
			if ! is_dry_run; then
				set -x
			fi
			$sh_c 'apt-get update -qq >/dev/null'
			$sh_c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $pre_reqs >/dev/null"
			$sh_c 'mkdir -p /etc/apt/keyrings && chmod -R 0755 /etc/apt/keyrings'
			$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg"
			$sh_c "chmod a+r /etc/apt/keyrings/docker.gpg"
			$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
			$sh_c 'apt-get update -qq >/dev/null'
		)
		pkg_version=""
		if [ -n "$VERSION" ]; then
			if is_dry_run; then
				echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
			else
				# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel
				pkg_pattern="$(echo "$VERSION" | sed "s/-ce-/~ce~.*/g" | sed "s/-/.*/g")"
				search_command="apt-cache madison 'docker-ce' | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
				pkg_version="$($sh_c "$search_command")"
				echo "INFO: Searching repository for VERSION '$VERSION'"
				echo "INFO: $search_command"
				if [ -z "$pkg_version" ]; then
					echo
					echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
					echo
					exit 1
				fi
				if version_gte "18.09"; then
					search_command="apt-cache madison 'docker-ce-cli' | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
					echo "INFO: $search_command"
					cli_pkg_version="=$($sh_c "$search_command")"
				fi
				pkg_version="=$pkg_version"
			fi
		fi
		(
			pkgs="docker-ce${pkg_version%=}"
			if version_gte "18.09"; then
				# older versions didn't ship the cli and containerd as separate packages
				pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io"
			fi
			if version_gte "20.10"; then
				pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
			fi
			if version_gte "23.0"; then
				pkgs="$pkgs docker-buildx-plugin"
			fi
			if ! is_dry_run; then
				set -x
			fi
			$sh_c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $pkgs >/dev/null"
			start_docker
		)
		echo_docker_as_nonroot
		exit 0
		;;
	centos | fedora | rhel | ol | rocky)
		# set vault.centos.or repo as CentOS8 is now EOL
		if [ "$lsb_dist" = "centos" ] && [ "$dist_version" -ge "8" ]; then
			$sh_c "find /etc/yum.repos.d -type f -exec sed -i 's/mirrorlist=http:\/\/mirrorlist.centos.org/\#mirrorlist=http:\/\/mirrorlist.centos.org/g' {} \;"
			$sh_c "find /etc/yum.repos.d -type f -exec sed -i 's/\#baseurl=http:\/\/mirror.centos.org/baseurl=http:\/\/vault.centos.org/g' {} \;"
			$sh_c "dnf swap centos-linux-repos centos-stream-repos -y"
		fi
		if [ "$lsb_dist" = "fedora" ]; then
			pkg_manager="dnf"
			config_manager="dnf config-manager"
			enable_channel_flag="--set-enabled"
			disable_channel_flag="--set-disabled"
			pre_reqs="dnf-plugins-core"
			pkg_suffix="fc$dist_version"
		else
			pkg_manager="yum"
			config_manager="yum-config-manager"
			enable_channel_flag="--enable"
			disable_channel_flag="--disable"
			pre_reqs="yum-utils"
			pkg_suffix="el"
		fi
		repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
		if [ "$lsb_dist" = "ol" ] || [ "$lsb_dist" = "rocky" ] || [ "$lsb_dist" = "rhel" ]; then
			repo_file_url="$DOWNLOAD_URL/linux/centos/$REPO_FILE"
		fi
		(
			if ! is_dry_run; then
				set -x
			fi
			$sh_c "$pkg_manager install -y -q $pre_reqs"
			$sh_c "$config_manager --add-repo $repo_file_url"

			if [ "$CHANNEL" != "stable" ]; then
				$sh_c "$config_manager $disable_channel_flag docker-ce-*"
				$sh_c "$config_manager $enable_channel_flag docker-ce-$CHANNEL"
			fi
			if [ "$lsb_dist" = "rhel" ] || [ "$lsb_dist" = "ol" ]; then
				adjust_repo_releasever "$dist_version"
				# Add extra repo for version 7.x
				if [[ "$dist_version" =~ "7." ]] || [ "$dist_version" == "7" ]; then
					if [ "$lsb_dist" = "rhel" ]; then
						$sh_c "$config_manager $enable_channel_flag rhui-REGION-rhel-server-extras"
						$sh_c "$config_manager $enable_channel_flag rhui-rhel-7-server-rhui-extras-rpms"
						$sh_c "$config_manager $enable_channel_flag rhui-rhel-7-for-arm-64-extras-rhui-rpms"
						$sh_c "$config_manager $enable_channel_flag rhel-7-server-rhui-extras-rpms"
						$sh_c "$config_manager $enable_channel_flag rhel-7-server-extras-rpms"
					else
						$sh_c "$config_manager $enable_channel_flag ol7_addons"
						# Adding OL7 developer repo if doesn't exist
						if [ "$(yum repolist | grep yum.oracle.com_repo_OracleLinux_OL7_developer >/dev/null || echo add)" == "add" ]; then
							$sh_c "$config_manager --add-repo https://yum.oracle.com/repo/OracleLinux/OL7/developer/x86_64"
						fi
					fi
				fi
			fi
			$sh_c "$pkg_manager makecache"
		)
		pkg_version=""
		if [ -n "$VERSION" ]; then
			if is_dry_run; then
				echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
			else
				pkg_pattern="$(echo "$VERSION" | sed "s/-ce-/\\\\.ce.*/g" | sed "s/-/.*/g").*$pkg_suffix"
				search_command="$pkg_manager list --showduplicates 'docker-ce' | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
				pkg_version="$($sh_c "$search_command")"
				echo "INFO: Searching repository for VERSION '$VERSION'"
				echo "INFO: $search_command"
				if [ -z "$pkg_version" ]; then
					echo
					echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
					echo
					exit 1
				fi
				if version_gte "18.09"; then
					# older versions don't support a cli package
					search_command="$pkg_manager list --showduplicates 'docker-ce-cli' | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
					cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
				fi
				# Cut out the epoch and prefix with a '-'
				pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
			fi
		fi
		(
			pkgs="docker-ce$pkg_version"
			if version_gte "18.09"; then
				# older versions didn't ship the cli and containerd as separate packages
				if [ -n "$cli_pkg_version" ]; then
					pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
				else
					pkgs="$pkgs docker-ce-cli containerd.io"
				fi
			fi
			if version_gte "20.10"; then
				pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
			fi
			if version_gte "23.0"; then
				pkgs="$pkgs docker-buildx-plugin"
			fi
			if ! is_dry_run; then
				set -x
			fi
			$sh_c "$pkg_manager install -y -q $pkgs"
		)
		echo_docker_as_nonroot
		exit 0
		;;
	sles)
		if [ "$(uname -m)" != "s390x" ]; then
			echo "Packages for SLES are currently only available for s390x"
			exit 1
		fi
		if [ "$dist_version" = "15.3" ]; then
			sles_version="SLE_15_SP3"
		else
			sles_version="SLE_15_SP2"
		fi
		opensuse_repo="https://download.opensuse.org/repositories/security:SELinux/$sles_version/security:SELinux.repo"
		repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
		pre_reqs="ca-certificates curl libseccomp2 awk"
		(
			if ! is_dry_run; then
				set -x
			fi
			$sh_c "zypper install -y $pre_reqs"
			$sh_c "zypper addrepo $repo_file_url"
			if ! is_dry_run; then
				cat >&2 <<-'EOF'
					WARNING!!
					openSUSE repository (https://download.opensuse.org/repositories/security:SELinux) will be enabled now.
					Do you wish to continue?
					You may press Ctrl+C now to abort this script.
				EOF
				(
					set -x
					sleep 30
				)
			fi
			$sh_c "zypper addrepo $opensuse_repo"
			$sh_c "zypper --gpg-auto-import-keys refresh"
			$sh_c "zypper lr -d"
		)
		pkg_version=""
		if [ -n "$VERSION" ]; then
			if is_dry_run; then
				echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
			else
				pkg_pattern="$(echo "$VERSION" | sed "s/-ce-/\\\\.ce.*/g" | sed "s/-/.*/g")"
				search_command="zypper search -s --match-exact 'docker-ce' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
				pkg_version="$($sh_c "$search_command")"
				echo "INFO: Searching repository for VERSION '$VERSION'"
				echo "INFO: $search_command"
				if [ -z "$pkg_version" ]; then
					echo
					echo "ERROR: '$VERSION' not found amongst zypper list results"
					echo
					exit 1
				fi
				search_command="zypper search -s --match-exact 'docker-ce-cli' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
				# It's okay for cli_pkg_version to be blank, since older versions don't support a cli package
				cli_pkg_version="$($sh_c "$search_command")"
				pkg_version="-$pkg_version"
			fi
		fi
		(
			pkgs="docker-ce$pkg_version"
			if version_gte "18.09"; then
				if [ -n "$cli_pkg_version" ]; then
					# older versions didn't ship the cli and containerd as separate packages
					pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
				else
					pkgs="$pkgs docker-ce-cli containerd.io"
				fi
			fi
			if version_gte "20.10"; then
				pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
			fi
			if version_gte "23.0"; then
				pkgs="$pkgs docker-buildx-plugin"
			fi
			if ! is_dry_run; then
				set -x
			fi
			$sh_c "zypper -q install -y $pkgs"
			if ! command_exists iptables; then
				$sh_c "$pkg_manager install -y -q iptables"
			fi
			start_docker
		)
		echo_docker_as_nonroot
		exit 0
		;;
	rancheros)
		(
			set -x
			$sh_c "sleep 3;ros engine list --update"
			engine_version="$(sudo ros engine list | awk '{print $2}' | grep "${docker_version}" | tail -n 1)"
			if [ "$engine_version" != "" ]; then
				$sh_c "ros engine switch -f $engine_version"
			fi
		)
		exit 0
		;;
	*)
		if [ -z "$lsb_dist" ]; then
			if is_darwin; then
				echo
				echo "ERROR: Unsupported operating system 'macOS'"
				echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
				echo
				exit 1
			fi
		fi
		echo
		echo "ERROR: Unsupported distribution '$lsb_dist'"
		echo
		exit 1
		;;
	esac
	exit 1
}

# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_install

为了防止安装后自动升级覆盖了docker,Ubuntu可以使用sudo apt-mark hold docker-ce docker-ce-cli docker-ce-rootless-extras锁定版本,其他发行版也有类似的命令。

自动化配置docker

将上面的脚本另存为docker-install-24.0.9.sh,然后再写一个脚本来完成docker的安装和配置:

#!/usr/bin/env bash
# init docker with composed

if ! type docker >/dev/null 2>&1; then
    max_try=3
    while true; do
        sudo bash docker-install-24.0.9.sh --mirror Aliyun
        if [ $? -eq 0 ]; then
            echo "succ to install docker"
            break
        else
            max_try=$((max_try - 1))
            if [ $max_try -eq 0 ]; then
                echo "can't install docker, exit"
                exit 1
            fi
            echo "fail to install docker, sleeping 5s before retrying"
            sleep 5
        fi
    done
fi

sudo mkdir -p /etc/docker
sudo rm /etc/docker/daemon.json >/dev/null 2>&1
sudo tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": [
        "https://docker.nju.edu.cn/",
        "https://mirror.baidubce.com"
    ],
    "live-restore": true,
    "log-driver":"json-file",
    "log-opts": {"max-size":"50m", "max-file":"3"}
}
EOF
sudo systemctl daemon-reload
sudo systemctl stop docker
systemctl enable docker

while true; do
    sudo systemctl start docker
    if [ $? -eq 0 ]; then
        echo "Docker start successfully"
        break
    else
        echo "Failed to restart Docker, sleeping for 5s before retrying..."
        sleep 5
    fi
done

这里主要是安装docker,配置国内镜像源和日志大小限制,然后重启docker服务。如果是在国外的机器上,可以不用配置镜像,但是日志大小限制一般还是需要配置的,否则服务日志可能会逐渐塞满硬盘。

安装k3s

参考如下脚本,需要注意的是--docker必须放在安装命令的最后面,否则不生效。

#!/usr/bin/env bash

MAX_POD_CNT=0
DATA_PATH=""
AGENT_MODE=0
SERVER_TOKEN=""
SERVER_IP=""
K3S_VERSION="v1.26.13+k3s2"
# 如果升级了k3s的版本,这里可以也跟着升级
HELM_VERSION="v3.12.3"
# 分布式存储插件
LONGHORN_VERSION="1.5.1"
NEED_LONGHORN=0
INIT_INSTALL=0
FORCE=0
export DEBIAN_FRONTEND=noninteractive

help() {
    echo "install script for k3s, you can run 'k3s-uninstall.sh' to uninstall"
    echo "Usage:"
    echo "  k3s_install.sh [-a] [-s server_url] [-t server_token] [-p max_pod_cnt] [-d data_path]"
    echo "  -a: install k3s as agent. Use it with -t <server token> and -s <server url>"
    echo "  -l: install longhorn"
    echo "  -t: token, for server or agent join cluster"
    echo "  -s: server ip"
    echo "  -f: force install"
    echo "  -p: max pod count, default is 110"
    echo "  -d: data path, default is ${DATA_PATH}"
    echo "  -v: k3s version, default is ${K3S_VERSION}"
    exit 0
}

while getopts 's:l:t:p:v:d:afh' OPT; do
    case $OPT in
    a) AGENT_MODE=1 ;;
    l) NEED_LONGHORN=1 ;;
    f) FORCE=1 ;;
    t) SERVER_TOKEN="$OPTARG" ;;
    s) SERVER_IP="$OPTARG" ;;
    p) MAX_POD_CNT="$OPTARG" ;;
    d) DATA_PATH="$OPTARG" ;;
    v) K3S_VERSION="$OPTARG" ;;
    h) help ;;
    ?) help ;;
    esac
done

# check if static ip
if ! ip route list default | grep static >/dev/null; then
    echo "you must set static ip before init env"
    exit 1
fi

# Check parameters
if [ $AGENT_MODE -eq 1 ] && { [ -z "$SERVER_IP" ] || [ -z "$SERVER_TOKEN" ]; }; then
    echo "miss server_ip or server_token for agent mode, exit"
    exit 1
fi

if [ -z "$SERVER_TOKEN" ] || [ -z "$SERVER_IP" ]; then
    INIT_INSTALL=1
fi

if [ $FORCE -eq 1 ] && type k3s >/dev/null 2>&1; then
    if k3s-uninstall.sh >/dev/null 2>&1; then
        echo "k3s uninstalled"
    else
        k3s-agent-uninstall.sh >/dev/null 2>&1
        echo "k3s agent uninstalled"
    fi
fi

# install k3s if not exist
if ! type k3s >/dev/null 2>&1; then
    # install k3s from now
    opts="K3S_KUBECONFIG_MODE=\"644\""
    cmd=""
    server_url="https://$SERVER_IP:6443"
    if [ $AGENT_MODE -eq 1 ]; then
        echo "install k3s as agent, server url is $server_url"
        opts="$opts K3S_URL=$server_url K3S_TOKEN=$SERVER_TOKEN"
    else
        if [ $INIT_INSTALL -eq 1 ]; then
            cmd="server --cluster-init"
        else
            opts="$opts K3S_TOKEN=$SERVER_TOKEN"
            cmd="server --server $server_url"
        fi
    fi
    # set to `--kube-proxy-arg=proxy-mode=ipvs`, if you want ipvs mode
    exec_opts=""
    if [ "$MAX_POD_CNT" -gt 0 ]; then
        exec_opts="$exec_opts --kubelet-arg=max-pods=$MAX_POD_CNT"
    fi
    if [ -n "$DATA_PATH" ] && [ $AGENT_MODE -eq 0 ]; then
        exec_opts="$exec_opts --default-local-storage-path=$DATA_PATH"
    fi
    if [ -n "$exec_opts" ]; then
        exec_opts=$(echo "$exec_opts" | xargs)
        opts="$opts INSTALL_K3S_EXEC='$exec_opts'"
    fi
    echo "install k3s online..."
    opts="$opts INSTALL_K3S_MIRROR=cn"
    if [ -n "$K3S_VERSION" ]; then
        opts="$opts INSTALL_K3S_VERSION=$K3S_VERSION"
    fi
    # --docker must at end!!!
    cmd="curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | $opts sh -s - $cmd --docker"
    echo "$cmd"
    eval "$cmd"
    if type k3s >/dev/null 2>&1; then
        echo "succ to install k3s"
    else
        echo "fail to install k3s"
        exit 1
    fi
else
    echo "skip k3s install..."
fi

if [ $AGENT_MODE -eq 0 ]; then
    echo "restart every 9 month for certificate rotate in server node"
    grep 'systemctl restart k3s' /etc/crontab || echo '0 2 1 */9 * systemctl restart k3s >/dev/null 2>&1' >>/etc/crontab
fi

if grep -q "KUBECONFIG" ~/.bashrc; then
    sed -i 's/export KUBECONFIG=.*/export KUBECONFIG=\/etc\/rancher\/k3s\/k3s.yaml/' ~/.bashrc
else
    echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >>~/.bashrc
fi

if ! grep -q "alias k='kubectl'" ~/.bashrc; then
    echo "alias k='kubectl'" >>~/.bashrc
fi

if ! type helm >/dev/null 2>&1; then
    echo "install helm..."
    curl https://csceciti-iot-devfile.oss-cn-shenzhen.aliyuncs.com/helm-install.sh | bash /dev/stdin -v $HELM_VERSION
    helm repo add bitnami https://charts.bitnami.com/bitnami
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
fi

if [ $AGENT_MODE -eq 1 ]; then
    echo "all done"
    exit 0
fi

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
# wait node ready
while true; do
    node_status=$(kubectl get node "$(hostname)" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
    if [[ "$node_status" == "True" ]]; then
        break
    else
        echo "wait until current node ready..."
        sleep 5
    fi
done

# new cluster
if [ $INIT_INSTALL -eq 1 ]; then
    # install longhorn
    if [ $NEED_LONGHORN -eq 1 ]; then
        helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace --version $LONGHORN_VERSION
        kubectl patch sc longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
        kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: longhorn-ingress
  namespace: longhorn-system
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: longhorn-system-middleware@kubernetescrd
spec:
  rules:
  - http:
      paths:
      - path: /longhorn
        pathType: Prefix
        backend:
          service:
            name: longhorn-frontend
            port:
              number: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: middleware
  namespace: longhorn-system
spec:
  replacePathRegex:
    regex: "^/longhorn(/|$)(.*)"
    replacement: "/${2}"
EOF
    fi
fi

echo "all done"

使用时注意

  • k3s安装到了root用户下,个人用户如果想要使用kubectl命令,建议cp /etc/rancher/k3s/k3s.yaml ~/.kube/config,如果使用export KUBECONFIG的方式,可能会提示权限问题,比较烦人;
  • k3s自带了kubectl命令,不过你也可以自己安装一个标准的kubectl
  • k3s有时候会莫名其妙的挂掉,建议用systemctl restart k3s重启一下;
  • 卸载k3s使用k3s-uninstall.sh即可;