安装docker
docker官方文档中写的很清楚:https://docs.docker.com/engine/install/
根据操作系统自行选择安装方式。
全篇以ubuntu 24.04为例
#卸载自带的冲突包。
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc | cut -f1)
使用apt安装
设置Docker的apt仓库
# 添加docker的官方GPG密钥
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# 添加apt软件源
#从下面开始直到EOF全部一次性复制。
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
#更新apt缓存
sudo apt update
#安装docker engine
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker基本安全配置
基本安全有以下几点:
- 使用获得了sudo权限的普通用户运行docker。
- docker容器内进程运行时指定以当前的普通用户执行(注意权限问题)。
- 配置文件设置只读权限。
- 端口映射到127.0.0.1。无法直接被外网访问,可通过ufw进行管理。
- 限制内存使用。
创建一个普通用户
#创建一个名为docrun的普通用户
useradd -m -s /bin/bash docrun
#设置密码
passwd docrun
#加入sudo用户组。
usermod -aG sudo docrun
加入docker用户组(不要进行此操作)
官方文档中对此操作有着明确警告https://docs.docker.com/engine/install/linux-postinstall
#除非你明确了解自己在干什么,否则不要执行此操作
#检查 Docker 用户组是否存在
sudo groupadd docker
#若不存在则创建
sudo groupadd docker
#将用户加入docker用户组
sudo usermod -aG docker docrun
创建或修改 /etc/docker/daemon.json 文件
vi /etc/docker/daemon.json
#写入以下配置
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
},
"live-restore": true,
"userland-proxy": false
}
- log-driver:指定 Docker 的默认日志驱动程序为 json-file。将容器的 stdout 和 stderr 输出以 JSON 格式输出。(路径
/var/lib/docker/containers/container-id/container-id-json.log) - log-opts:日志设置,max-size 20m 每个容器的日志文件最大为20MB,之后新建日志文件。max-file 3 日志文件最多为3个。超过时循环旧文件。
- live-restore:值为true时启用 live restore 功能,也就是升级、重启docker。。。。时,正在运行的容器不会停止。
- userland-proxy:值为false时禁用 Docker 的用户空间代理,性能会稍微好一点。若想使用自定义IPv6网桥时删除这一项。否则IPv6网桥不可用。
保存退出。应用配置。
sudo systemctl daemon-reload
sudo systemctl restart docker
切换用户,退出当前shell。重新用新建的docrun用户登录
运行容器
映射的目录提前由docrun用户创建好,否则可能会出现权限管理混乱。
由于docker会在ufw上打洞,所以端口映射时不要直接映射端口号。而是把容器内的端口号映射到127.0.0.1:xx之后通过安装在宿主机上的nginx反代。如果nginx也要安装在docker中,不要这么操作。
以nginx镜像(非 Root版本)为例。本例只演示镜像运行的用户权限。
sudo docker run -d \
--name my-nginx \
--restart unless-stopped \
--user $(id -u):$(id -g) \
-p 127.0.0.1:8080:8080 \
-v $(pwd)/html:/usr/share/nginx/html:ro \
--memory="100m" \
nginxinc/nginx-unprivileged:alpine
- docker run -d :创建并启动一个新容器,-d在后台运行。
- –name :给容器命名为
my-nginx。 - –restart :容器重启策略,
unless-stopped表示主机重启时容器自动启动。可手动关闭容器。 - –user $(id -u):$(id -g) :以宿主机当前的用户身份运行。
$(id -u),获取当前用户的用户 ID,$(id -g),获取当前用户组 ID。但对容器内强制以root运行的进程无效。不要在nginx这类需要root权限的镜像中添加这类命令,会导致容器无限重启。 - -p :端口绑定宿主机的127.0.0.1:8080绑定到容器内的80端口。防止docker在ufw上打洞。但这样外网无法直接访问。
- -v :挂载卷。
$(pwd),当前目录。/usr/share/nginx/html,容器内nginx默认网站root目录。:ro,容器只能读取这个目录,不能修改。 - –memory=”100m” : 限制容器最多使用100MB内存。
- nginx:alpine:镜像名和标签
查看容器运行状态
#查看容器中镜像运行状态
sudo docker exec my-nginx ps aux
#查看宿主机容器运行状态
sudo docker top my-nginx
#或者
ps aux | grep nginx
可以看到除了nginx的uid,为当前用户的uid。

docker升级版安全配置
配置文件中加入"userns-remap": "default"。别的没有太大差别。
"userns-remap": "default"会把容器内的root用户(UID 0)映射为宿主机的非特权用户(例如UID 100000),但挂载目录时非常麻烦。目录的权限是个坑。有些需要 –privileged 或特殊权限的容器可能无法正常工作。
修改配置文件
vi /etc/docker/daemon.json
#写入以下配置。
#顺便再加入IPv6内网支持和exec-opts
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
},
"live-restore": true,
"ipv6": true,
"ip6tables": true,
"userns-remap": "default"
}
- exec-opts:统一管理入口为systemd。Ubuntu或debain尽量添加此配置。
- ipv6: 启动 IPv6。
- fixed-cidr-v6:配置本地地址
- ip6tables:开启 IPv6 防火墙规则管理
- userns-remap:用户命名空间重映射
保存退出。应用配置。
sudo systemctl daemon-reload
sudo systemctl restart docker
#确认基准UID
cat /etc/subuid
cat /etc/subgid
图中新创建的dockremap:100000:65536便是”userns-remap”: “default”模式下docker的UID范围。100000为起始UID。同时也是root的UID。65536为UID数量。

创建ipv6网桥
#创建名为my_ipv6_net的IPv6网桥
sudo docker network create \
--driver bridge \
--ipv6 \
--subnet=fd00:1:2:3::/64 \
my_ipv6_net
#检查当前的网络状态
sudo docker network ls

可以看到名为my_ipv6_net的网络已经创建成功。
运行容器
#运行容器
sudo docker run -d \
--name my-nginx \
--network my_ipv6_net \
--restart unless-stopped \
--user 1000:1000 \
-p 127.0.0.1:80:80 \
-p 127.0.0.1:443:443 \
-p [::1]:80:80 \
-p [::1]:443:443 \
-v $(pwd)/html:/usr/share/nginx/html:ro \
--memory="100m" \
nginx:alpine
之后查看docker容器的PID。
#查看容器本身的UID
sudo ps aux | grep nginx
#查看容器内进程的UID
sudo docker exec my-nginx ps aux

此次并没有映射文件。nginx本身没有需要读取的文件,所以不会报错。若挂载了需要读取的文件,会因为权限问题无法运行。使用ACL解决。
添加挂载文件读取权限
上一步已经明白了docker nginx容器的UID为100101。
准备映射的目录
#目录放在一个名为dockerun用户的家目录中。
#进入目录
cd /home/dockerun/
#创建名为nginx的目录
mkdir nginx
#创建名为conf.d的目录
mkdir nginx/conf.d
#创建名为log的目录
mkdir nginx/log
#创建名为html的目录
mkdir nginx/html
创建nginx配置文件
vi /home/dockerun/nginx/nginx.conf
#写入以下内容
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
#保存退出
#继续创建nginx配置文件
vi /home/dockerun/nginx/conf.d/default.conf
#写入以下内容
server {
listen 80;
listen [::]:80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
location / {
try_files $uri $uri/ /index.html;
}
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
}
#保存退出
接下来若直接执行sudo docker run…….,日志中会提示权限问题,导致nginx无限重启
#查看日志
sudo docker logs my-nginx
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
使用ACL赋予nginx目录权限
严格来说,应该以最小权限为原则。但是实在懒的一个一个配置。索性整个目录都配置上权限。
nginx运行时会分别使用root和普通user,所以要把root的100000和普通user的100101都配置上。
#赋予容器内user用户的读写执行权。
sudo setfacl -R -m u:100101:rwx /home/dockerun/nginx
sudo setfacl -R -m d:u:100101:rwx /home/dockerun/nginx
#赋予容器内root用户的读写执行权。
sudo setfacl -R -m u:100000:rwx /home/dockerun/nginx
sudo setfacl -R -m d:u:100000:rwx /home/dockerun/nginx
#检查权限
getfacl /home/dockerun/nginx
可以看到user:100000:rwx user:100101:rwx,表示UID为100000和100101的用户对nginx目录有着读写执行(rwx)的权限。

运行容器
sudo docker run -d \
--name my-nginx \
--network my_ipv6_net \
--restart unless-stopped \
-p 127.0.0.1:80:80 \
-p 127.0.0.1:443:443 \
-p [::1]:80:80 \
-p [::1]:443:443 \
-v /home/dockerun/nginx/html:/usr/share/nginx/html:ro \
-v /home/dockerun/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /home/dockerun/nginx/conf.d:/etc/nginx/conf.d:ro \
-v /home/dockerun/nginx/log:/var/log/nginx \
--memory="100m" \
nginx:alpine
检查运行状态
#这回不再报错了
sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c6695a1db925 nginx:alpine "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 127.0.0.1:80->80/tcp, [::1]:80->80/tcp, 127.0.0.1:443->443/tcp, [::1]:443->443/tcp my-nginx
#测试IPv4端口映射
#403是因为nginx的网站root目录本身就是空的。
dockerun@Ubuntutest:~$ curl "http://127.0.0.1:80"
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.29.4</center>
</body>
</html>
#测试IPv6端口映射
dockerun@Ubuntutest:~$ curl "http://[::1]:80"
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.29.4</center>
</body>
</html>
配置完成。
另外,设置了”userns-remap”: “default”后会对部分管理docker的容器造成麻烦。此时可用命令(--userns=host)强制对某个容器单独禁用命名空间重映射。
#此处以portainer-ce为例
docker volume create portainer_data
docker run -d \
-p 8000:8000 \
-p 9443:9443 \
--name portainer \
--restart=always \
--userns=host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:lts
rootless模式
先决条件
rootless模式是官方最为推荐的运行模式,完全不需要root权限。但也正因为如此,存在很多限制。例如不能使用低于1024的端口号等。详情参阅官方文档:https://docs.docker.com/engine/security/rootless/以及第三方文档https://rootlesscontaine.rs/getting-started/
已安装docker
首先创建一个新用户,这个用户运行docker时没必要赋予sudo权限。但配置环境时仍然需要sudo。
#创建一个名为rootlesstest的用户,用户名可随意指定
useradd -m -s /bin/bash rootlesstest
#设置密码
passwd rootlesstest
安装必要的依赖。
sudo apt update
sudo apt install uidmap
sudo apt install -y dbus-user-session
确认用户命名空间范围。
切换到新注册的用户。
#切换用户
su - rootlesstest
#查看用户UID。
id -u
#会显示1002。不同用户,UID会不同。
1002
#产看当前用户名。
whoami
#会显示当前的用户名。
rootlesstest
#查看当前用户的UID,GID起始编号和范围。
#确保用户至少有65536个ID。
grep ^$(whoami): /etc/subuid
rootless:231072:65536
grep ^$(whoami): /etc/subgid
rootless:231072:65536
如果原版docker已经运行,可以考虑禁用它
sudo systemctl disable --now docker.service docker.socket
sudo rm /var/run/docker.sock
安装docker rootless
因为已经用apt安装了docker。docker中本身包含了安装脚本。直接运行脚本即可。
无systemctl模式
注意!如果使用su切换到rootlesstest用户运行脚本,会导致安装后没有systemctl。此时可用手动配置环境即可。
#运行此脚本
dockerd-rootless-setuptool.sh install
#如果脚本不存在,需要手动安装
sudo apt-get install -y docker-ce-rootless-extras
#之后脚本会提示
rootlesstest@Ubuntutest:~$ dockerd-rootless-setuptool.sh install
[INFO] systemd not detected, dockerd-rootless.sh needs to be started manually:
PATH=/usr/bin:/sbin:/usr/sbin:$PATH dockerd-rootless.sh
[INFO] Creating CLI context "rootless"
Successfully created context "rootless"
[INFO] Using CLI context "rootless"
Current context is now "rootless"
[INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc):
# WARNING: systemd not found. You have to remove XDG_RUNTIME_DIR manually on every logout.
export XDG_RUNTIME_DIR=/home/rootlesstest/.docker/run
export PATH=/usr/bin:$PATH
[INFO] Some applications may require the following environment variable too:
export DOCKER_HOST=unix:///home/rootlesstest/.docker/run/docker.sock
由于rootlesstest是用su切换的用户,没有systemctl,所以根据提示,添加环境变量,启动docker。由于是手动配置环境变量,重启,或重新登录后仍然需要配置,所以还要进行持久化配置。
首先测试docker rootless模式运行。
设置环境变量
export XDG_RUNTIME_DIR=/home/rootlesstest/.docker/run
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///home/rootlesstest/.docker/run/docker.sock
测试docker
#直接启动,启动后各种日志信息会显示在终端。
PATH=/usr/bin:/sbin:/usr/sbin:$PATH dockerd-rootless.sh
#在后台启动
PATH=/usr/bin:/sbin:/usr/sbin:$PATH dockerd-rootless.sh > /dev/null 2>&1 &
#之后测试是否启动成功
docker ps
配置持久化
在rootlesstest的家目录中执行以下命令,编辑.bashrc文件
vi ~/.bashrc
#最下方添加以下配置
export XDG_RUNTIME_DIR=/home/rootlesstest/.docker/run
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///home/rootlesstest/.docker/run/docker.sock
#保存退出
#应用配置
source ~/.bashrc
#启动和之前一样还是用以下命令
PATH=/usr/bin:/sbin:/usr/sbin:$PATH dockerd-rootless.sh > /dev/null 2>&1 &
#关闭docker
pkill dockerd
systemctl模式
安装方法和上面一样,唯一的不同是不要用su -切换用户,新开个ssh终端,用rootlesstest用户重新登陆。
#若之前安装并配置了持久化,先删除之前的配置
vi ~/.bashrc
#删除之前添加的内容
export XDG_RUNTIME_DIR=/home/rootlesstest/.docker/run
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///home/rootlesstest/.docker/run/docker.sock
#刷新配置
source ~/.bashrc
#执行安装脚本
dockerd-rootless-setuptool.sh install
#安装成功会有类似提示
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger rootlesstest`
[INFO] CLI context "rootless" already exists
[INFO] Using CLI context "rootless"
Current context is now "rootless"
[INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc):
export PATH=/usr/bin:$PATH
[INFO] Some applications may require the following environment variable too:
export DOCKER_HOST=unix:///run/user/1002/docker.sock
安装成功,配置环境变量。
同样编辑~/.bashrc文件
vi ~/.bashrc
#把安装的提示信息写入文件
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/1002/docker.sock
#保存退出
#应用配置
source ~/.bashrc
未安装docker
以新用户dockerun安装。
未安装docker时,可用脚本安装。Ubuntu 24.04 及更高版本需要先配置AppArmor。官方给的脚本在ubuntu24.04中一般用户执行可能会提示“权限不足”。给出了修改后的脚本,此脚本需要sudo权限。
#官方脚本
filename=$(echo $HOME/bin/rootlesskit | sed -e 's@^/@@' -e 's@/@.@g')
[ ! -z "${filename}" ] && sudo cat <<EOF > /etc/apparmor.d/${filename}
abi <abi/4.0>,
include <tunables/global>
"$HOME/bin/rootlesskit" flags=(unconfined) {
userns,
include if exists <local/${filename}>
}
EOF
修改后的脚本
filename=$(echo $HOME/bin/rootlesskit | sed -e 's@^/@@' -e 's@/@.@g')
[ ! -z "${filename}" ] && cat <<EOF | sudo tee /etc/apparmor.d/${filename}
abi <abi/4.0>,
include <tunables/global>
"$HOME/bin/rootlesskit" flags=(unconfined) {
userns,
include if exists <local/${filename}>
}
EOF
之后重启apparmor.service服务
sudo systemctl restart apparmor.service
安装docker rootless
#使用脚本安装
curl -fsSL https://get.docker.com/rootless | sh
#安装成功
+ systemctl --user enable docker.service
Created symlink /home/dockerun/.config/systemd/user/default.target.wants/docker.service → /home/dockerun/.config/systemd/user/docker.service.
[INFO] Installed docker.service successfully.
[INFO] To control docker.service, run: `systemctl --user (start|stop|restart) docker.service`
[INFO] To run docker.service on system startup, run: `sudo loginctl enable-linger dockerun`
[INFO] Creating CLI context "rootless"
Successfully created context "rootless"
[INFO] Using CLI context "rootless"
Current context is now "rootless"
[INFO] Make sure the following environment variable(s) are set (or add them to ~/.bashrc):
export PATH=/home/dockerun/bin:$PATH
[INFO] Some applications may require the following environment variable too:
export DOCKER_HOST=unix:///run/user/1001/docker.sock
设置环境变量
注意,要根据安装成功的提示设置对应的变量,不要照抄。
#编辑bashrc文件
vi ~/.bashrc
#最下方添加以下内容
export PATH=/home/dockerun/bin:$PATH
export DOCKER_HOST=unix:///run/user/1001/docker.sock
#保存退出
#应用配置
source ~/.bashrc
#之后便可以正常使用docker了
运行docker rootless
查看docker信息,一般用户使用,无需sudo权限。
docker info
rootless模式systemctl操作
- 启动docker:
systemctl --user start docker - 关闭docker:
systemctl --user stop docker - 查看docker状态:
systemctl --user status docker - 重启docker:
systemctl --user restart docker - 设置登录启动(登录rootlesstest用户后自动启动):
systemctl --user enable docker - 设置开机启动(开机,而无需登录便启动,需要sudo):
sudo loginctl enable-linger $(whoami)
如果不想给rootlesstest用户sudo权限,可以让有sudo权限的用户设置sudo loginctl enable-linger rootlesstest
配置文件位置
docker rootless的配置文件不在/etc/docker/daemon.json中。位置是~/.config/docker/daemon.json。若没有,需要自己创建。
修改后重启docker服务即可systemctl --user restart docker
测试镜像
#下载nginx镜像进行测试
docker run -d -p 8080:80 nginx
#浏览器打开8080端口能看到nginx默认主页,或者用curl命令
curl 127.0.0.1:8080
#将会看到
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>