【OPS.0x00】使用 Docker 创建隔离的工作环境

本文最后更新于:2023年9月26日 下午

怎么实验室只有👴一个运维

0x00. 一切开始之前

因为笔者目前所在的实验室好像只有笔者一个人会一点点运维技术,大家都喜欢在根目录和各种乱七八糟的目录到处乱拉(错乱),为了保证服务器环境不会哪天用着用着给整个挂掉了,笔者写了一份《服务器安全使用指北》,并顺便拷贝过来水一篇博客(笑)

What is docker?

抄自 Wikipedia:

Docker 是一个开源的开放平台软件,用于开发应用、交付(shipping)应用和运行应用。Docker 允许用户将基础设施(Infrastructure)中的应用单独分割出来,形成更小的颗粒(容器),从而提高交付软件的速度。

按照笔者的理解,Docker 可以简单理解为用于创建共享操作系统内核虚拟机隔离环境(称为容器)的工具包:

Why use docker?

为了解决服务器环境污染问题(例如配环境把服务器主环境搞崩了,这将同时影响到使用服务器的多个用户),在服务器多人共享的情况下,对于不涉及内核驱动更改的工作,推荐使用 docker 创建一个新的隔离环境来完成。

How to install docker?

推荐参照官网的安装教程来完成,这里笔者也是使用的官网教程进行安装

笔者实验室服务器安装了 Ubuntu,其他发行版请参见官网教程进行安装

Docker Engine(推荐)

首先通过如下命令卸载可能存在的旧版 docker:

1
$ for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

接下来添加 docker 仓库的 GPG key 并进行配置:

1
2
3
4
5
6
7
8
9
10
$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update

正式安装 docker:

1
$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

完成之后可以通过如下命令验证是否安装成功:

1
$ sudo docker run hello-world

将当前用户添加到 docker 用户组:

1
2
3
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ sudo systemctl restart docker

Docker Desktop(不推荐)

Docker Desktop 额外提供了一个 GUI,以及不同用户间的隔离机制,不过 笔者用着感觉不是很 ok ,所以还是推荐传统的纯 Docker Engine

对于非 Gnome 桌面环境而言,需要安装 gnome-terminal

1
$ sudo apt install gnome-terminal

卸载可能安装过的 docker:

1
2
3
4
$ sudo apt remove docker-desktop
$ rm -r $HOME/.docker/desktop
$ sudo rm /usr/local/bin/com.docker.cli
$ sudo apt purge docker-desktop

设置 docker 仓库:

1
2
3
4
5
6
7
8
9
10
$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update

下载 docker 安装包:

1
$ wget https://desktop.docker.com/linux/main/amd64/docker-desktop-4.22.1-amd64.deb

安装 docker:

1
2
$ sudo apt-get update
$ sudo apt-get install ./docker-desktop-4.22.1-amd64.deb

启动 docker:

1
$ systemctl --user start docker-desktop

将当前用户添加到 docker 用户组:

1
2
3
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ systemctl --user restart docker-desktop

0x01. 使用 docker 搭建基本的容器环境

若用户未加入 docker 用户组则所有命令开头都需要添加 sudo 获得 root 权限

创建 Docker 镜像

首先在一个空白的文件夹中创建一个名为 Dockerfile 的文件,写入如下内容:

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
FROM ubuntu:22.04

ARG DEBIAN_FRONTEND=noninteractive

# 可以在这里预先添加想要安装的软件
RUN apt-get -y update && \
apt-get install -y lib32z1 apt-transport-https python3 git \
libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev \
vim netcat openssh-server unzip make wget bison flex build-essential curl

# 开启ssh登录
RUN rm -f /etc/service/sshd/down
RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config &&\
sed -ri 's/#UseDNS\ no/UseDNS\ no/g' /etc/ssh/sshd_config && \
sed -ri "s/StrictModes yes/StrictModes no/g" /etc/ssh/sshd_config && \
sed -ri "s/UsePAM yes/UsePAM no/g" /etc/ssh/sshd_config

# 设置允许密码登录
RUN echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config

# 添加用户密码,这里只是一个示例,根据你的需求自己修改
RUN groupadd arttnba3 && \
useradd -g arttnba3 arttnba3 -m && \
echo "arttnba3:lTaa4cqucW4zxvJswU5KH0HIRM9VmjO7" | chpasswd && \
echo "root:rqUHmfGUtjItWapDuv3NEqwQ9bm6GBrs" | chpasswd

# 添加 ssh 登录,注意换成自己新建的用户的用户名
RUN mkdir /home/arttnba3/.ssh && \
echo "这里写你的ssh公钥" > /home/arttnba3/.ssh/authorized_keys

# 保持 docker 容器运行
RUN echo "#!/bin/sh\nservice ssh restart\nsleep infinity" > /root/start.sh
RUN chmod +x /root/start.sh

CMD ["/root/start.sh"]

EXPOSE 22

接下来运行以下命令创建镜像:

1
$ docker build -t "自定义镜像名" .

创建并启动 docker 容器

完成 docker 镜像创建之后接下来我们就可以开始使用该镜像启动一个新的容器,使用如下命令:

1
$ docker run -d --privileged -p "外部端口:22" -h "容器主机名" --name="容器名" 自定义镜像名

这里添加了一个服务器端口到容器内部 22 端口的映射,从而使得可以从外部直接通过 ssh 连接到容器内部

如果你的容器不需要主机相关的一些权限,也可以去掉 --privileged 参数

进入 docker 容器

进入容器环境有两种办法,一种是通过 container id(可以通过 docker ps 查看,在创建容器时也会打印出容器id),另一种是通过自定义的容器名,这里进入容器后默认是 root 用户:

1
$ docker exec -it 容器名或容器ID /bin/bash

如果想要以容器内部的指定用户身份进入容器,使用如下命令:

1
$ docker exec -u 用户名 -it 容器名或容器ID /bin/bash

从外部连接 docker 容器

在启动容器时我们已经指定了一个外部端口映射,可以直接通过该端口进行 ssh 连接,操作和正常登录服务器基本一致,不过要手动指定该外部端口:

1
$ ssh 容器内用户名@服务器主机名或IP -p 自定义的外部端口

删除 docker 容器

如果 docker 内环境配崩了不想要了,可以简单通过如下命令将该容器进行删除:

1
2
$ docker stop 容器ID或容器名
$ docker rm 容器ID或容器名

重新创建新的容器环境可以直接回到第二步继续进行

删除 docker 镜像

如果 docker 镜像构建感觉不够好,可以通过如下命令删除对应镜像(需确保没有容器在使用该镜像):

1
$ docker rmi 镜像名

后续使用则需要回到第一步从头开始

0x02. 创建可以使用显卡的 docker 环境

有的时候会存在需要在显卡上跑一些任务的需求,本节讲述如何创建一个挂载了显卡的 docker 容器

What is NVIDIA Container Toolkit ?

NVIDIA Container Toolkit 是由英伟达开发的一套对 docker 进行包装的工具,从而使得 docker 容器可以使用服务器主机上的显卡

image.png

安装 NVIDIA Container Toolk

有两种方法:

方法 ①

需要在服务器主机上完成,首先使用如下命令安装基本组件:

1
2
$ sudo apt-get update \
&& sudo apt-get install -y nvidia-container-toolkit-base

使用如下命令检查是否安装成功:

1
$ nvidia-ctk --version

接下来使用如下命令生成 CDI (Container Device Interface)规范文件:

1
$ sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

添加 NVIDIA 仓库与 GPG 密钥:

1
2
3
4
5
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

安装 toolkit:

1
2
$ sudo apt-get update
$ sudo apt-get install -y nvidia-container-toolkit

配置 docker daemon 进程以识别 NVIDIA COntainer Runtime:

1
2
$ sudo nvidia-ctk runtime configure --runtime=docker
$ sudo systemctl restart docker

测试是否安装成功:

1
$ sudo docker run --rm --runtime=nvidia --gpus all nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi

最后成功输出和 nvidia-smi 命令一样的结果就说明安装成功了:

image.png

方法 ②

参见 Github 上的这个 issue

若方法 ① 没法成功进行,可以使用如下命令进行安装:

1
2
3
4
5
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
$ sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
$ sudo systemctl restart docker

添加 NVIDIA Rumtime

NVIDIA Container Runtime 的添加一共有三种方法

方法 ①

首先创建配置文件夹:

1
$ sudo mkdir -p /etc/systemd/system/docker.service.d

然后创建配置文件,注意这条命令一共有五行,全部复制后粘贴进去:

1
2
3
4
5
$ sudo tee /etc/systemd/system/docker.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --host=fd:// --add-runtime=nvidia=/usr/bin/nvidia-container-runtime
EOF

重启 docker:

1
$ sudo systemctl daemon-reload && sudo systemctl restart docker

方法 ②

Nvidia toolkit 安装成功之后也可以辅助我们完成这一步,通过如下两条命令其中之一完成:

1
2
$ sudo nvidia-ctk runtime configure --runtime=docker
$ sudo nvidia-ctk runtime configure --runtime=docker --set-as-default # 设置为默认

重启 docker:

1
$ sudo systemctl restart docker

方法 ③

也可以使用 docker 直接添加 NVIDIA Runtime:

1
$ sudo dockerd --add-runtime=nvidia=/usr/bin/nvidia-container-runtime [...]

配置带 CUDA 的 docker 镜像

创建并启动带显卡的 Docker 容器

0x01 中的创建 docker 容器的步骤基本相同,不过需要添加额外的参数

更多用法参见 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/user-guide.html,本文仅展示基本用法

使用 Nvidia Runtime

  • 容器内挂载所有的显卡

通过额外添加 --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all 参数完成:

1
$ docker run -d --privileged -p "外部端口:22" -h "容器主机名" --name="容器名" --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all 自定义镜像名
  • 容器内挂载指定显卡

通过额外添加 --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=显卡标号 参数完成,其中显卡标号为 nvidia-smi -L 命令中所展示的序号,挂载多张显卡需要用 , 进行隔离(显卡标号间不要加空格 ),这里以挂载第一、二张显卡为例:

1
$ docker run -d --privileged -p "外部端口:22" -h "容器主机名" --name="容器名" --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=0,1 自定义镜像名

传统用法

  • 容器内挂载所有的显卡

额外添加 --gpus all 参数即可:

1
$ docker run -d --privileged -p "外部端口:22" -h "容器主机名" --name="容器名" --gpus all 自定义镜像名
  • 容器内挂载指定显卡

通过额外添加 --gpus='"device=显卡标号"' 参数完成,其中显卡标号为 nvidia-smi -L 命令中所展示的序号,以下命令以第一张显卡为例:

1
$ docker run -d --privileged -p "外部端口:22" -h "容器主机名" --name="容器名" --gpus='"device=0"' 自定义镜像名

【OPS.0x00】使用 Docker 创建隔离的工作环境
https://arttnba3.github.io/2023/08/31/OPS-0X00-DOCKER_ON_SERVER/
作者
arttnba3
发布于
2023年8月31日
许可协议