背景

我校的操作系统课程设计是斯坦福大学的 pintos 项目(当然只需要完成少量的 project)。pintos 需要在 x86 架构的 Linux 系统上运行,但是我最近主力使用的是一台 Macbook Pro,我不太愿意每天背着两台电脑去图书馆。

我原本的设想是把我运行 Ubuntu 机器放在宿舍,通过校园网 ssh 连接到它。但是这样做有两个问题:

  1. 校园网的网络环境不太好,即使是使用代理也无法保证一些软件源的稳定连接。
  2. 学校最近在进行消防安全检查,不允许离开宿舍时不断电源,而我没法保证宿舍时刻都有人。

看起来,我只能在 Mac 上跑一个虚拟机了;然而我不太喜欢使用虚拟机的使用体验:在运行时占用太多的内存,启动或者关闭需要等待太长的时间。我希望有一个更轻量的解决方案。

最近,我关注的 up 主 @TheCW 在沉寂了一年之后忽然更新了一个视频。我重新回看了他之前发布的几个视频后,忽然发现他在 Mac 上需要一个 Linux 环境时,直接用 docker run 了一个全新的 Ubuntu 镜像,这个方法看起来非常轻巧——非常适合我。

下文是我在 Mac 上使用 docker 运行 Ubuntu 的步骤。

安装 Docker Desktop for Mac

访问 Docker 官网 下载 Docker Desktop for Mac 即可,这部分不再赘述。

下载并运行 Ubuntu 镜像

在终端中输入以下命令来拉取 Ubuntu 镜像:

docker pull --platform linux/amd64 ubuntu

我使用 –platform 来指定镜像的架构,因为我需要一个 x86 架构的镜像;如果直接使用 docker pull ubuntu,docker 会默认自动选择当前机器的架构,这对我来说当然是不行的。

拉取完成后,运行以下命令来启动一个新的 Ubuntu 容器:

docker run --platform linux/amd64 -it ubuntu /bin/bash

这个命令会启动一个新的 Ubuntu 容器,并且进入 bash shell。不过,这个容器还是一个全新的、仅包含最基本软件的 Ubuntu 系统,它看起来应该是这样的:

wood@Woods-MacBook-Pro ~> docker exec -it ubuntu bash
root@8d907eeb-----:/# 

如果需要退出容器,使用 exit 命令。

如果要重新进入容器,可以使用 docker ps -a 查看容器的 ID,然后使用 docker start <container-id>docker attach <container-id> 命令。

我们首先应该要新建一个用户,因为 root 用户不应该用于日常操作。在容器中依次以下命令:

apt update
apt install sudo adduser
adduser <your-username>

如果你在 apt update 时遇到了网络问题,可以尝试更换软件源,或者使用代理(参看代理 Docker 容器)。

在按照提示设置密码和其他信息后,我们需要将新建的用户加入 sudo 组:

usermod -aG sudo <your-username>

使用 su 命令切换到新建的用户,并测试一下 sudo 是否正常工作:

su - <your-username>
sudo whoami

如果输出为 root,则说明 sudo 已经正常工作。

Docker 快捷命令

在继续配置 Ubuntu 容器之前,我想先让启动和退出容器更加方便。

首先使用 docker ps -a 查看容器的 ID,然后使用 docker rename <container-id> ubuntu 命令为容器设置别名为 ubuntu。

随后,我在 ~/.config/fish/config.fish 中添加了以下两个 alias:

alias ubuntu 'docker start ubuntu && docker attach ubuntu'
alias ubuntu-stop 'docker stop ubuntu'

运行 source ~/.config/fish/config.fish 使配置生效。

ps: 如果你使用的是 bash,可以将 alias 添加到 ~/.bashrc 中,并运行 source ~/.bashrc,注意,bash 的 alias 语法和 fish 有所不同,需要使用一个 = 号:alias ubuntu='docker start ubuntu && docker attach ubuntu'

配置 Ubuntu 容器

fish shell

我习惯使用 fish shell,并以 fish 为默认 shell。在容器中,我首先安装了 fish shell:

sudo apt install fish

我没有使用 chsh 命令来更改默认 shell,而是直接修改了对 ubuntu 命令的 alias:

alias ubuntu "docker start ubuntu && \
docker exec -it --user <your-username> \
ubuntu fish -C 'cd ~'"

这样,每次进入容器时,都会以普通用户的身份进入 fish shell,并且进入到用户目录。

到这里,我已经接近了日常使用 Ubuntu 的体验。

screenfetch

我喜欢登陆时看到 screenfetch,诚实地说,是因为这满足了我小时候对“黑客”的幻想。

我需要在 Docker 容器中安装 screenfetch,然后在 fish 的配置文件中添加 screenfetch 命令。

sudo apt install screenfetch
echo "screenfetch 2>/dev/null" >> ~/.config/fish/config.fish

这里,因为 screenfetch 无法访问宿主机的信息,会显示一行错误信息,所以我在 screenfetch 命令后面加了一个 2>/dev/null 来丢弃错误信息。

我说,谁能拒绝一个 screenfetch 呢?

screenfetch

配置 Docker 容器代理

注意,这不是代理 Docker 的软件源,而是代理 Docker 容器内的网络。

编辑 ~/.docker/config.json 文件,添加以下内容:

{
  "proxies": {
    "default": {
     "httpProxy": "http://proxy.example.com:8080",
     "httpsProxy": "http://proxy.example.com:8080"
    }
  }
}

这里的 httpProxyhttpsProxy 需要根据你实际的代理设置进行修改。