安装 linux

之前已经在真机上安装过双系统 Windows & arch linux 过了(Grub)(不过因为稳定性原因还是删了),虚拟机那些也已经是有的(CentOS,Ubuntu),WSL 也有(Kali),云主机暂时没有不过也不是很常用就不折腾这个了吧。现在主要环境转到 MacOS 去了,所以环境上本来也是类 UNIX 环境了(笑)。当然还是有挺多不一样的,比如最近才知道 /proc 这个目录竟然是没有的,需要专门的命令去获取进程的各种信息,这点还是不大一样。

考虑到在 Mac 上确实还没有装过很原汁原味的 linux 环境,也考虑到 Mac 本身内核就是和 linux 兼容的情况,所以我打算直接使用 docker 在进程等级隔离并模拟 linux 环境。

官方文档: https://docs.docker.com/reference/cli/docker/

先下好 docker.app,然后一路安装注册,最后有个在设定里的命令行提示可以打开

安装 Ubuntu 镜像,第一步先拉取一个 image

  ~ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
Digest: sha256:72297848456d5d37d1262630108ab308d3e9ec7ed1c3286a32fe09856619a782
Status: Image is up to date for ubuntu:latest
docker.io/library/ubuntu:latest
 

这里可以配置一些 volume 放持久化的数据,不过想着每次用完即刻删掉也行(毕竟不会涉及太大型的软件),就不配置了

第二步,打开一个 container,让它在后台跑起来(一直 sleep 防止退出),并进行端口映射,之后方便我们远程连接

  ~ docker run -d -p 2222:22 ubuntu sleep infinity
8be0e3d95c4e43bee5a5568edcc06a938a5819d28b1e0c60350cc7f03f8c9d11
  ~ docker ps -a                                  
CONTAINER ID   IMAGE     COMMAND            CREATED          STATUS          PORTS                  NAMES
8be0e3d95c4e   ubuntu    "sleep infinity"   12 seconds ago   Up 11 seconds   0.0.0.0:2222->22/tcp   bold_haibt

第三步,安装一些必要的工具

➜  ~ docker exec -it 8be0e3d95c4e bash
root@8be0e3d95c4e:/# apt update
0% [Connecting to ports.ubuntu.com (198.18.2.194)]^C
root@8be0e3d95c4e:/# apt update
Get:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease [256 kB]
...            
Get:17 http://ports.ubuntu.com/ubuntu-ports noble-security/restricted arm64 Packages [984 kB]                         
Fetched 28.2 MB in 7s (3810 kB/s)                                                                                     
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
18 packages can be upgraded. Run 'apt list --upgradable' to see them.
root@8be0e3d95c4e:/# apt update && apt install -y openssh-server

之后再打开 ssh 服务,配置一些东西,然后用 ssh 连上就行了,算了还是使用 dockerfile 来完成这个过程,防止后续 image 变得臃肿

编写 dockerfile 如下:

FROM ubuntu
 
RUN apt update && apt install -y \
    openssh-server \
    gcc \
    gdb \
    make \
    build-essential \
    vim \
    && rm -rf /var/lib/apt/lists/*
 
# 设置密码,配置 SSH
RUN mkdir /var/run/sshd \
    && echo 'root:root' | chpasswd \  
    && sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config \
    && sed -i 's/UsePAM yes/UsePAM no/' /etc/ssh/sshd_config \  
    && ssh-keygen -A
 
EXPOSE 22
 
# 启动 SSH 服务(-D 保持前台运行)
CMD ["/usr/sbin/sshd", "-D"]

构建 image

docker build -t rabbit . 

docker run -d -p 2222:22 rabbit               
8d76362e69c2edf0f3f59bf24ed73da5018fb962a452837663a691b05c202955
ssh root@localhost -p 2222
The authenticity of host '[localhost]:2222 ([::1]:2222)' can't be established.
ED25519 key fingerprint is SHA256:HWeKcKwpuww+WtuzH3A7cH1RpJ4WzSgMxq/WFVwcRUY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Warning: Permanently added '[localhost]:2222' (ED25519) to the list of known hosts.
root@localhost's password: 
root@8d76362e69c2:~# 

成功连上

这里可以再使用 vscode 扩展远程连接,过程其实和上面差不多,只是图形化界面了

如果要停止,用 docker stop 命令即可

为了使用方便,做到即用即弃,可以用一个小脚本函数实现功能

run_in_rabbit() {
    docker run -it --rm \
        -v "$(pwd):/workspace" \
        -w /workspace \
        rabbit \
        "$@"
}

这样就可以直接在当前目录下的文件用 linux 环境执行程序了

固定 ip & 远程连接

查看当前 ip 情况,这个是桥接模式下的

root@8d76362e69c2:~# apt update && apt install -y iproute2

root@8d76362e69c2:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
...
25: eth0@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
       
root@8d76362e69c2:~# ip route
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

这里手动配置 ip 会和 docker 的 IPAM 冲突,还是通过 docker 指定 IP。安装 Docker 后,会自动创建一个名为 docker0 的虚拟网桥(类似交换机)。每个容器启动时,Docker 会创建一个 veth pair (虚拟网卡对),一端在容器内( eth0 ),另一端连接到 docker0 网桥。

容器访问外网,流量从容器的 eth0 接口发出,通过 veth pair 连接到宿主机的 docker0 网桥,进入宿主机的网络栈。宿主机通过 iptables 的 NAT 规则 ,将流量的源 IP (容器 IP 172.17.0.2 )转换为 宿主机的物理网卡 IP (如 192.168.1.100 )。转换后的流量通过宿主机的物理网卡发送上网

这里我们通过 docker network 可以生产一个新的虚拟网桥,让它拥有独立的 ip 地址池,iptable,子网等等。然后再分配一个静态的 ip 地址给它。

➜  ~ docker network create --driver=bridge --subnet=10.10.0.0/24 --gateway=10.10.0.1 rabit_net 
20b456a211c608d2d3a68ced27832ec319083ebf8b0ac74e028730868cc49154

➜  ~ docker run -d -p 2222:22 --network rabit_net --ip 10.10.0.114 rabbit        
dd46c9af2e69960c2e40df0101b1521335c14cc0fe20678c03aebe8be9e35d8c

使用 ssh 连接(端口转发)

ssh root@localhost -p 2222
root@localhost's password: 
root@8b8b058e9930:~# 

成功连接上了,这时查看 ip 地址,就是我们修改过后的地址了

root@8b8b058e9930:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
...
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:0a:0a:00:72 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.10.0.114/24 brd 10.10.0.255 scope global eth0
       valid_lft forever preferred_lft forever

不过遗憾的是,Docker Desktop 的设计导致宿主机无法直接访问 HyperKit 虚拟机内部的子网,还是不能够直接通过 ip 来访问,而是采用了端口转发的方式完成

简单的命令探索

都老熟人了,没啥好探索的

mv --help                 
mv: illegal option -- -
usage: mv [-f | -i | -n] [-hv] source target
       mv [-f | -i | -n] [-v] source ... directory
cp --help                                           
cp: illegal option -- -
usage: cp [-R [-H | -L | -P]] [-fi | -n] [-aclpSsvXx] source_file target_file
       cp [-R [-H | -L | -P]] [-fi | -n] [-aclpSsvXx] source_file ... target_directory

...

cat file.txt  
less file.txt 
head -n 5 file.txt 
tail -f log.txt 

总之就是一个 man--help ,遇到问题时搜索 “How to do X in Unix”,根据报错信息调整命令吧