目录

离线安装开箱即用的容器运行环境运行环境

文章简介:离线安装各种容器引擎,docker/containerd+nerdctl

背景

通常每个 linux 发行版都会自带的包管理安装 docker,但这种安装方式在 to b 完全离线环境下适配不同种 linux 发行版安装成本比较高。docker 由 golang 开发,依赖都是捆绑到一起的,直接复制二进制文件即可完成部署,简单便捷。Docker 官方介绍了一种 Install Docker Engine from binaries 方法,但是这种方式不完整:This page contains information on how to install Docker using binaries. These instructions are mostly suitable for testing purposes. We do not recommend installing Docker using binaries in production environments as they don't have automatic security updates.。本文将介绍一种生产可用的离线安装 docker、且跨 linux 发行版的方法。

方法 1: 安装官方的 docker 离线包

Docker 官方文档中提供的方法 使用的方法是 sudo dockerd & ,没有服务保活,不适用与生产使用。在这个基础上,完善一下保活基本就可以了。

如下为 package.sh,打包一个离线安装包

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env bash

set -o errtrace
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace

cd "$(dirname "$0")"

function debug() {
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:DEBU: $*" >&2
}

function info() {
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:INFO: $*" >&2
}

function err() {
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:ERRR: $*" >&2
}

DOCKER_VERSION="${DOCKER_VERSION:-"26.1.1"}"
DOCKRE_MIRROR=${DOCKRE_MIRROR:-"https://mirrors.tuna.tsinghua.edu.cn"}

function machine_arch() {
    arch=$(uname -m)
    case $arch in
        x86_64)
            echo amd64
            ;;
        aarch64)
            echo arm64
            ;;
        *)
            echo $arch
            ;;
    esac
}

function docker_arch() {
	arch=$(uname -m)
    case $arch in
        arm64)
            echo aarch64
            ;;
        *)
            echo $arch
            ;;
    esac
}

# https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/static/stable/aarch64/docker-26.1.1.tgz
DOCKER_DOWNLOAD_URL="${DOCKRE_MIRROR}/docker-ce/linux/static/stable/$(docker_arch)/docker-${DOCKER_VERSION}.tgz"

BASEPATH=./installer
mkdir -p $BASEPATH

pushd $BASEPATH
wget $DOCKER_DOWNLOAD_URL
popd

mkdir -p $BASEPATH/docker
cat > $BASEPATH/docker/daemon.json <<'EOF'
{
  "live-restore": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "200m"
  }
}
EOF

mkdir -p $BASEPATH/systemd
cat > $BASEPATH/systemd/containerd.service <<'EOF'
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd

Restart=always
RestartSec=2
TimeoutSec=30
StartLimitInterval=0

KillMode=process
Delegate=yes
LimitNOFILE=1048576
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# TasksMax=infinity

[Install]
WantedBy=multi-user.target
EOF

cat > $BASEPATH/systemd/docker.service <<'EOF'
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service
Wants=network-online.target
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/local/bin/dockerd -H fd://
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=30
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
# retry start always
StartLimitInterval=0

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this option.
# TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

cat > $BASEPATH/systemd/docker.socket <<'EOF'
[Unit]
Description=Docker Socket for the API
PartOf=docker.service

[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF

tar -zvcf dockerbin-installer.tar.gz installer install.sh

其中 install.sh 内容为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/env bash

set -o errtrace
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace

cd "$(dirname "$0")"

BASEPATH=./installer
DOCKER_BIN_DIR=/usr/local/bin

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

function debug() {
	echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:DEBU: $*" >&2
}

function info() {
	echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:INFO: $*" >&2
}

function err() {
	echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]:ERRR: $*" >&2
}

user="$(id -un 2>/dev/null || true)"
sh_c='sh -c'
if [ "$user" != 'root' ]; then
	cat >&2 <<-'EOF'
		Error: this installer needs the ability to run commands as root.
		You can exec `sudo su`, and reinstall.
	EOF
	exit 1
fi

stat /run/systemd || (echo "not systemd"; exit 1)
docker version && exit 0

mkdir -p $DOCKER_BIN_DIR
tar xvf $BASEPATH/docker-*.tgz --strip-components 1 -C $DOCKER_BIN_DIR

mkdir -p /etc/docker
cp $BASEPATH/docker/* /etc/docker/

cp $BASEPATH/systemd/* /etc/systemd/system

sync
groupadd -f docker || true
systemctl daemon-reload
systemctl enable docker
systemctl restart docker

另外,如果需要使用 docker-compose,可以从 docker/compose 主页下载静态编译的 docker-compose 即可。

方法 2: nerdctl

docker 的内存占用相对来说还是过于复杂,进程会包含 containerd + dockerd,并且 dockerd 本身就包含了自己的网络实现和 overlay 存储实现。而 containerd + ctr 即可完成对日常容器镜像的管理等工作,再配合 nerdctl 兼容部分 docker 的命令行参数,如果只是在类似云主机中跑服务的场景,完全可以使用此方法替换。

nerdctl 的安装非常简单,再 nerdctl release 中下载nerdctl-full-*-linux-*.tar.gz, ``

1
2
3
4
5
6
7
wget -O nerdctl-full-1.7.6-linux-amd64.tar.gz https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-full-1.7.6-linux-amd64.tar.gz

tar -zvxf nerdctl-full-*-linux-amd64.tar.gz -C /usr/local

sudo systemctl enable --now containerd
sudo systemctl enable --now buildkit # 如果需要 docker build,则需要启动他
nerdctl run --debug-full -d --name nginx -p 80:80 nginx:alpine

nerdctl 包含 compose 命令,可以直接使用。

方法 3: truenas scale 中使用类 docker 的体验

truenas scale 的小伙伴会了解到,truenas scale 中实际启动了一个单节点的 k3s 作为容器引擎。很多小伙伴反馈 k3s 的学习门槛过高了。truenas scale 虽然是基于 debian 的 linux 发行版,实际使用的包管理不是 apt,且不能使用 apt 安装其他的包,具体信息如下:

1
2
3
4
5
apt
Package management tools are disabled on TrueNAS appliances.

Attempting to update SCALE with apt or methods other than the SCALE web
interface can result in a nonfunctional system.

经过调研,可以使用 这个项目 ,使用 nerdctl 作为 客户端访问隔离环境中的 containerd,做到与 方法 2 中描述的相同使用体验。

再我的 truenas 中,我的工作流是本地使用 docker-compose 将需要配置的服务写好,然后 rsync 到 trunenas 上,nerdctl compose up -d 完事。如果添加的 alias docker=nerdctl, 几乎可以忽略掉 nerdctl 的存在,当成 docker 使用即可。

总结

如上,请欢快的使用 docker / containerd 吧。