安裝 QEMU/KVM & Docker

No Comments

目前初步評估/學習了一下,不少人建議使用 qemu-kvm 來作為虛擬機器因其使用體驗或許會優於 virtualbox。不過 qemu 顯示方面目前筆者認知就是 spice & vnc 兩種用戶終端,後者速度劣於 spice。而跨平台的顯示終端又以 RDP 為常見且使用體驗也或許優於 spice,其或許也有遠端顯示的能力/見參考資料,但是否有友善的終端就待學習後才能得知了。故當前筆者結論還是會持續使用 virtualbox/rdp 於遠端顯示與操作。qemu-kvm 則用於本地端的實務使用。docker 則毌需贅言有幾乎取代各種 VM 的態勢;話說回來 qemu 此類 VM 仍有其不可取代性。
就待筆者慢慢習得/將補充常見用法於下/主要也都出處於以下參考資料就不再強調。

參考資料

# 一。一般安裝 qemu/kvm:
sudo apt install qemu qemu-kvm libvirt-daemon libvirt-daemon-system libvirt-clients virtinst virt-manager cpu-checker bridge-utils


# 二。最小安裝示例:

## install
sudo apt install qemu-system-x86

## create an image as a virtual disk(alternatively use the qemu-img tool)
fallocate -l 10G image.img

## generate the vm and attach the cdrom/for system install
qemu-system-x86_64 -enable-kvm -cdrom ubuntu-20.04.5-desktop-amd64.iso -boot menu=on -drive file=image.img,format=raw -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on

## run the vm after the system on vm installed/remove the -cdrom
qemu-system-x86_64 -enable-kvm -boot menu=on -drive file=image.img,format=raw -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on

# 三。最小安裝示例後,將 raw image 轉換成 qcow2 sparse file:

## install guestfs-tools
sudo apt install guestfs-tools

## convert to qcow2
sudo virt-sparsify image.img --convert qcow2 image.qcow2

## alter the file mode and owner to the same as image.img

## launch the vm
qemu-system-x86_64 -enable-kvm -boot menu=on -drive file=image.qcow2 -m 4G -cpu host -smp 2 -vga virtio -display sdl,gl=on

# Note:1. 較新的 VM 版本,可能是沒有 qemu-kvm package 了,而是納入 qemu,故只使用參數 -enable-kvm。2. libvirt/virt-manager 應是有包含了 vm disk 的相關工具的集成,故不見得只安裝 guestfs-tools 會較簡化。

# 四。以上第二點,僅使用 qemu-kvm,即可在系統上使用 VM。第三點是額外的工具,處理映像檔相關的問題。然而如此的配置是相當的陽春,例如 client/host 的 copy/paste,遠端桌面等都須要額外的加裝才能 support。而若使用 virt-manager 等 libvirt 相關的工具,便是可以一應俱全了。因此看需求來走什麼樣的安裝。筆者接下來又安裝了 spice-vdagent。後續下達的 command 也列在下。

## qemu-system-x86_64 -enable-kvm -machine vmport=off -boot menu=on -drive file=image.qcow2 -m 4G -cpu host -smp 2 -vga qxl -display sdl,gl=on -spice port=3001,disable-ticketing=on -device intel-hda -device hda-duplex -device virtio-serial -chardev spicevmc,id=vdagent,debug=0,name=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0

## 使用遠端桌面則下例如,remote-viewer spice://localhost:3001

## 然而這樣的加裝無法“順利”滿足需求。因此想要較完整且“不沾鍋”的做法,就是一般的安裝 qemu/kvm,libvirtd,virt-manager,而 libvirtd 在要使用時才啓動。而遠端桌面則上面連結有位德國人的文章有很完整與全面的說明與實務。而較快速的方法就曾提過的改用 virtualbox rdp 方案了。另提,底下的 qemu base image,在 machine-id 的部份關聯到 dhcp ip,只對使用 virt-manager 才有效。

# 五。若還要再精簡地安裝,則只安裝,sudo apt install --no-install-recommends qemu-system-x86 qemu-system-gui guestfs-tools,以可製作 image 並執行之;或再更進一步地只要,
sudo apt install --no-install-recommends qemu-system-x86 qemu-system-gui,則就可以執行既存的 images。

# 六。

## 補充一。
vmware/virtualbox/qemu 可共用的 disk file format 之一為 VMDK。但在這幾種平台間流用多少可能會發生不一致性的問題,例如網路:筆者將 virtualbox 的 VMDK 用到了 qmeu 上,網路不通,解法是 client 內改一下 netplan 改成 NetworkManager 管理即可。但於此的前提是我們並沒有在 client 內再安裝歸屬 virtualbox 專有的東西/若有則網路必衝突不能用了因為此 client 的外部界接界面不再是 virtualbox,亦謂了少了這部份;若然,將所裝者移除看看/筆者未試。
network:
  version: 2
  renderer: NetworkManager

## 補充二。
在 qemu 上啟用 port forward 如下(5555-to-22;其中 network device e1000 在 virtualbox & qmeu 皆有支援):
qemu-system-x86_64 -enable-kvm -drive file=/home/ken/Ubuntu.vmdk -m 2G -cpu host -smp 2 -vga virtio -device driver=e1000,netdev=user.0 -netdev user,id=user.0,hostfwd=tcp::5555-:22
則可以在 host 下以如此登入:ssh -p 5555 ken@127.0.0.1,又在此前提下/ssh 登入使用,可加 -nographic 參數以成 headless(在 client 內則改顯示模式 graphical -> multi-user)。
qemu-system-x86_64 -enable-kvm -drive file=Ubuntu.vmdk -m 2G -cpu host -nographic -device driver=e1000,netdev=user.0 -netdev user,id=user.0,hostfwd=tcp::5555-:22

再來還可使用 ssh tunnel 導到本地端,即跑 VM 的機器在遠端,原本我們該 ssh 進遠端,再 ssh localhost 進 VM:
forward:  ssh -L [local mapped port]:[remote service address]:[remote service port] user@ssh-server -p ssh-server-service-port
backward: ssh -R [remote mapped port]:[local service address]:[local service port] user@ssh-server -p ssh-server-service-port

例如 [client] VM ssh service is localhost:5555,[host] ssh-server 不使用 port 22 而是使用 5900。我們 mapping 回來 local port 3000(3000 forwarding 過去)則,
ssh -L 3000:localhost:5555 host_user@ssh-server -p 5900
不僅開啟了此 host ssh,
那麼我們在本地端再下,
ssh -p 3000 vm_user@localhost
也即在本地端開啟了 VM ssh。
於此建立一個通道但是是兩個 services,即 ssh 本身(在遠端存取)和走 ssh 的遠端服務(在本地端存取)。而只將 ssh 結束並不會結束服務,需和服務斷線,通道才會關閉。

同理,-R 則是讓遠端可以存取本地端的 service。
並且也都繞過了防火牆。
也舉個例子:
ssh -o serveraliveinterval=300 -R 3000:localhost:22 ken@[remote-host] -p 5900
遠端的 sshd 在 remote-host:5900;本地端 sshd 為 localhost:22。則於本地端執行該指令將在遠端以使用者 ken 的身份在其主機上的 port 3000 上建立本地端所映射過去的 sshd。即,
若本地端位於區網內,即便使用者在外遊蕩,仍能透過此遠端(public access),進入此遠端,存取該本地端。

這意謂著,兩端的各自的區網內的服務都可如此來被另一端存取到,只要二者中有一有至少一個對外網路(ssh 得到)且其具有 ssh-server 即可(但就要另一端下指令)。
又或者“-R”的使用下,兩端皆需具有 ssh-server。
如此,極端地說,遠端(即表示它有 public domain)是可以存取到任一本地端內的任一服務,即便本地端無 public domain 且藏得多麼“區網”/區網下的區網,只要此節點有 ssh-server,即可以 ssh-client,以“-R”連到遠端再回連回來本地端服務,其展現在/mapping 到遠端的某一 local-port。

而 ssh-server/client 僅是建立經過認證且加密的通道。故任何以 port 做為 service 的服務都可以使用,例如 database 而非僅是 shell/ftp 而已。此前提下,services 輕易透傳到 anywhere,例如遠端桌面,資料庫,網頁伺服器,VPN 等,可說相當便利且強大。

## 補充三。
結果,這一段落全都在介紹 ssh tunnel。。。
前面提到,一台網路䁥名機,即無公開的 hostname,且無固定的 ip;因而不能夠被循正常管道登入該機。那麼,我們就是在該機上建立 R-tunnel,以致於在公開的遠端機上可以反向登入該䁥名機。
但,理所當然,這樣的方式是必須在䁥名機上處理 R-tunnel 的建立。除非除了該䁥名機一直開著之前提下,該 tunnel 能夠一直保持著連線且遠端機沒做過 reboot/一旦 reboot,tunnel 被迫開閉,這就表示必須回去䁥名機身邊重建 tunnel。
因此,該要解決的問題是,在䁥名機上,一旦 tunnel 斷線,就必須被自動重新建立。故,我們使用 autossh 處理斷線重連以及 systemd 負責 reboot 後執行。
故,使用下一段 service 就行了。並請參考以下連結。
https://askubuntu.com/questions/45679/ssh-connection-problem-with-host-key-verification-failed-error
https://askubuntu.com/questions/947841/start-autossh-on-system-startup
https://askubuntu.com/questions/757779/auto-start-a-reverse-ssh-tunnel-on-system-startup

######
[Unit]
Description=AutoSSH tunnel service to remote ken@[hostname]:5900 port 3000, if local-ssh uses port 22.
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR 3000:localhost:22 -o "StrictHostKeyChecking no" -i /home/ken/.ssh/id_rsa ken@[hostname] -p 5900 uptime

[Install]
WantedBy=multi-user.target
######

二。製作 qemu base image

  • 摘錄自這篇文章
    https://octetz.com/docs/2020/2020-10-19-machine-images/
  • 我們通常會製作常用的 base images,用以 clone 出來使用。不過若使用某原生 image 來同時派生執行多個實例,則因原生的 images 都已有設定例如 hostname 的資料,將會造成混淆。其次 machine-id 也會一樣,將可能導致 client ips 被指定成相同的 ip。因此尚須將製作出來的原生 image 加工過,使得同時執行的多個 images 實例不再衝突。
  • 當我們將原生 image 製作完成,並於該系統運行下再接著加工。
  • 有三個步驟,當中二個分別是刪除 hostname,及 machine-id。第三個是産生亂數所産生的 hostname,細節如下:
  • create hostname-init.sh in /usr/local/bin/
#!/bin/sh
SN="hostname-init"

# do nothing if /etc/hostname exists
if [ -f "/etc/hostname" ]; then
  echo "${SN}: /etc/hostname exists; noop"
  exit
fi

echo "${SN}: creating hostname"

# set hostname
HN=$(head -60 /dev/urandom | tr -dc 'a-z' | fold -w 3 | head -n 1)
echo ${HN} > /etc/hostname
echo "${SN}: hostname (${HN}) created"

# sort of dangerous, but works.
if [ -f "/etc/hostname" ]; then
  /sbin/reboot
fi
  • sudo chmod a+x /usr/local/bin/hostname-init.sh
  • sudo chown root:root /usr/local/bin/hostname-init.sh
  • create /etc/systemd/system/hostname-init.service
[Unit]
Description=Set a random hostname.
ConditionPathExists=!/etc/hostname

[Service]
ExecStart=/usr/local/bin/hostname-init.sh

[Install]
WantedBy=multi-user.target
  • sudo chmod 644 /etc/systemd/system/hostname-init.service
  • sudo systemctl daemon-reload
  • sudo systemctl enable hostname-init
  • sudo -i
  • echo -n > /etc/machine-id
  • rm -v /etc/hostname
  • exit
  • 接著就關機。base image 就完成了。並將其 clone 出來使用。
  • 經實際測試發現,clone 出來的 images 佔用空間會小於來源檔。按這篇文章的加工方法,其是一年多前的做法;因此筆者猜想/不再實測,使用 virt-clone 指令或許現今已自動排除衝突的可能,讀者可自行驗測看看。
  • 補充:
    當 image file 的磁碟空間不敷使用時,可以擴容,且還蠻快速完成的,如下兩行指令。其中 old.qcow2 是來源的 image file,new.qcow2 是新生成的。而假設其 root “/” 位於 old.qcow2 檔案系統內的 /dev/sda3。將擴成 20G。
    qemu-img create -f qcow2 new.qcow2 20G
    virt-resize –expand /dev/sda3 old.qcow2 new.qcow2(注意 – – expand)
# 其中,一些指令用法如下,

# list the managed images
virsh list --all

# Clone the_u20 to another_node0
virt-clone --original the_u20 \
    --name another_node0 \
    --file /var/lib/libvirt/images/node0.qcow2

# Power on the new clone
virsh start another_node0

# Find the IP addresses; the network name can be others; default is default
virsh net-dhcp-leases default

Docker

  • 簡介之,
  • 在 Ubuntu 下,docker 已是另一個應用程式的名稱,故使用 docker.io 安裝名。
  • 使用時,因 docker 使用 root 權限級別的 socket,故需另創建 docker group 以避開 sudos 讓其他使用者可以使用。
  • 若創建及設置好之後 user 使用 docker 仍出現錯誤訊息,就是有資料在更早之前因執行 sudo 而設定了,此時只要將此目錄刪掉即解。
    ~/.docker/
  • docker 是常駐兩支 daemons,docker & containerd,可用 systemd 手動啟動或解除之。
# add a new group docker, note to check first if it already exists
sudo groupadd docker

# add the $USER to docker group
sudo usermod -aG docker $USER

[re-login to take effect]

# run an example
docker run hello-world
  • 一些常用的 docker commands 列於下,這是看 Nana 的教學影片整理出來的。會持續追加中。
  • https://www.youtube.com/@TechWorldwithNana/videos
  • 另外注意到,前面我們有介紹了 ssh tunnel。現在,假設這樣的一個安裝配置,我們在主機上安裝 VMs,而在 VM 中才使用 docker,
  • 那麼,在此 VM 下的某個 container,所提供的 service,例如建立在 port 443,那麼在主機上是無法存取到此服務的。因此,藉由 VM 下已安裝的 ssh server 的服務,透過 Qemu(本篇)/virtualbox(前文章 virtualbox port forwarding),便可把 ssh server service 提取出來讓 host 可見可用。換言之,我們在 host 下便可利用 ssh tunnel 存取到 container service 了。
  • ssh -L [host-new-port]:[service_address]:[service_port] ssh_user@localhost -p [port-forwarded-by-qemu]
### basic ###

# docker pull [image to download]

# docker ps (show running containers)

# docker ps -a (running or had run/history)

# docker images (show local images) = image ls

# docker image rm [-f] [image-name]

# docker rmi [image-name]

# docker rm [container-name]

# docker run image-name[:tag] (create new container)

# docker run -d [image] (detach mode)

# docker run -p[host-port-binding]:[container-port] [image]

# docker run --name (naming this container) [image]

# docker stop [cid/c-name]

# docker start [cid/c-name]

# docker logs [cid/c-name]

# docker exec (run something inside this container) -it (interactive terminal) [cid/c-name] /bin/bash (the one to execute. and this one is the 1st thing to do)

# docker network ls

# docker network create [network-name]

# -e [environment-variable-assignment]
# --net [network-name]

# docker-compose -f [filename.yaml] [up/down]

# docker build -t [image-name]:[tag] [Dockerfile location]

# docker run -v [host storage location]:[container mapped location] # host volume
# docker run -v [the storage location in container] # anonymous volume
# docker run -v [some name]:[the storage location in container] # named volume, good to use

# docker run -d -p[host mapped port]:[registry service port] -v [host the path for storage]:[/var/lib/registry] --name [naming this registry] registry[:tag]
# query which images in the registry
    curl -X GET http://[registry address:port]/v2/_catalog
# query the tags by a image in the registry
    curl -X GET http://[registry]/v2/[image-name]/tags/list
# query the detailed info of a image in the registry
    curl -X GET http://[registry]/v2/[image]/manifests/[tag]
# query by using hyper/docker-registry-web

# docker tag [the original image name:tag] [registry domain address:port]/[new image name:tag]
# docker push [registry address:port]/[new image name:tag]

# docker pull [registry address:port]/[image name][:tag]


### docker-compose filename.yaml (network will be auto setup) ###
version
services
    [container names]:
        image
        ports
        volumes
        environment
[list of volume names if defined any]


### Dockerfile for building docker image ###
FROM
ENV
RUN [commands inside this container]
COPY src dst (host copy command)
CMD ["command", "parameter"] (only used for a start command inside this container, for bring up this container. aka docker run/start)

20221228 更新 - 完整範例

以下,將從安裝系統開始,
筆者的目的是希望,
同時具有 docker 及 docker private repository/registry,都是建立在 VM 之上。並同時具備有 CLI & GUI 介面。此外,全都要放到 RAM 上面去跑,因此 size 小是必要的。
簡單評估了 linux distributions,alpine linux 應是可選之一。故,首先建立基礎的 alpine VM,做為 cli image base。再來加入 alpine desktop,作為另一支 gui base image。
從無例外地,現下只做到將 alpine desktop 建立起來,因此於此就需要先註記下來了,也是因為是筆者首次安裝非 ubuntu 的 linux operation system distribution。當前的 alpine 版本是 3.17,有了桌面後大小是大約 700MB,有別於 ubuntu18.04 最小也要近 4GB。


# 配置磁碟
fallocate -l 12G image
virt-sparsify image --convert qcow2 alpine_s3.17.qcow2
rm image

# 啟動 iso。請留意顯示器的參數不太一樣。
qemu-system-x86_64 -enable-kvm -boot menu=on -cdrom alpine-standard-3.17.0-x86_64.iso -drive file=alpine_s3.17.qcow2 -m 2G -cpu host -smp 4 -vga virtio -device intel-hda -device hda-duplex -device virtio-serial -chardev spicevmc,id=vdagent,debug=0,name=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0 -device driver=e1000,netdev=user.0 -netdev user,id=user.0,hostfwd=tcp::5555-:22

# 以 root 無密碼登入,並執行安裝。而後便輕易完成 alpine 系統的安裝。
setup-alpine
# Alpine std 3.17.

qemu-system-x86_64 -enable-kvm -drive file=alpine_s3.17.qcow2 -m 2G -cpu host -smp 2 -vga virtio -display sdl,gl=on -device intel-hda -device hda-duplex -device virtio-serial -chardev spicevmc,id=vdagent,debug=0,name=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0 -device driver=e1000,netdev=user.0 -netdev user,id=user.0,hostfwd=tcp::5555-:22
### 加入使用者。
### 另外於此特別提醒,從筆者架站至今,也至少有三年了,(故經驗下),系統和 wordpress 的 login name,請不要使用像 ken 這樣簡單明確。要使用像 coolken,uglyken 等 login-name,同時 user-name 不能等同 login-name,而是使用像 ken 這樣有別於 login-name。一來暴力破解將兩倍困難,因為 login 就要被猜一次(故現下您便想到,絕不要開放 root 登入)。再來,彼方嘗試過 login name 不存在,便會放棄闖入以釋放佔用的頻寬。

adduser -h /home/ken -s /bin/ash ken

### 建立 wheel 群組,若不存在建立之

### 將 ken 加入 wheel group
adduser ken wheel

### 安裝 doas
apk add doas

### 測試 doas 組態檔文法是否正確
doas -C doas.conf && echo "ok" || echo "error"

### 放置 doas 的路徑,我試了好久XD
/etc/doas.d/

### doas.conf 內容,將使用者 ken 加入 administrators,使得 ken@doas 用法等同 sudo。
permit persist :wheel as root

### 將 doas ln -s 成 sudo

### apk repository 內容(用法:add/update/upgrade/list/info/search)
#/media/cdrom/apks

http://alpine.ccns.ncku.edu.tw/alpine/latest-stable/main
http://alpine.ccns.ncku.edu.tw/alpine/latest-stable/community

#http://alpine.ccns.ncku.edu.tw/alpine/v3.17/main
#http://alpine.ccns.ncku.edu.tw/alpine/v3.17/community
#http://alpine.ccns.ncku.edu.tw/alpine/edge/main
#http://alpine.ccns.ncku.edu.tw/alpine/edge/community
#http://alpine.ccns.ncku.edu.tw/alpine/edge/testing

### 記得要查詢一下,啟用 sshd
rc-status default/sysinit/...
rc-update add sshd
rc-service sshd restart

### 至此 console base image 應是配置完成了

### 接著安裝 alpine-desktop,找不到該套件,只能手動安裝

setup-xorg-base

# 接著出現錯誤訊息,hwdrivers, mdev 沒有啟動在 sysinit。
rc-update add hwdrivers sysinit
rc-update add mdev sysinit
rc-service hwdrivers start
rc-service mdev start
#之後再重跑一次上一步

### Desktop 使用 XFCE
apk add xfce4 xfce4-terminal [xfce4-screensaver] lightdm-gtk-greeter

### 啟用開機後自動進入 desktop
rc-update add dbus
rc-update add lightdm
reboot

### 註,後續筆者安裝了 x2goserver,不過無法使用,故再重新安裝 mate,可參考這篇,
https://www.linuxshelltips.com/install-mate-alpine-linux/
並截錄指令如下,
sudo apk add mate-desktop-environment dbus dbus-x11 lxdm adwaita-icon-theme faenza-icon-theme
gvfs_pkgs=$(apk search gvfs -q | grep -v '\-dev' | grep -v '\-lang' | grep -v '\-doc' | grep -v '\-dbg')
sudo apk add $gvfs_pkgs
ttfs=$(apk search -q ttf- | grep -v '\-doc' | grep -v '\-dbg' | grep -v 'anonymous' | grep -v 'awesome')
sudo apk add $ttfs
sudo rc-update add dbus
sudo rc-update add lxdm

但仍無法使用,問題可能出在 freenx,可參考這篇,
https://forums.centos.org/viewtopic.php?f=14&t=5525,
所以一旦 freenx 的問題解了,應該使用 xfce 會相當輕量化。

### host/client 的 copy/paste 無作用,原因未知。

### 原則上目前已能順利使用。查看一下 ~/.xsession_error,會有很多錯誤,有興趣可以去解解看。

### 參考 https://docs.alpinelinux.org/user-handbook/0.1a/Working/openrc.html

### Alpine 會有 glibc 相容性的問題,須處理,但不見得結果相容無虞。參考此連結解法。
https://stackoverflow.com/questions/66963068/docker-alpine-executable-binary-not-found-even-if-in-path

### 參考
https://superuser.com/questions/628169/how-to-share-a-directory-with-the-host-without-networking-in-qemu
https://superuser.com/questions/502205/libvirt-9p-kvm-mount-in-fstab-fails-to-mount-at-boot-time
啓用 shared folder 的設定,貼在此,
qemu 的 option:-virtfs local,path=/tmp/,mount_tag=my_shared_folder,security_model=passthrough,id=my_shared_folder
(id 可能是用在 disk uuid)
fstab line:my_shared_folder   /whatever-local-mounted-folder    9p      trans=virtio,version=9p2000.L   0 0
( 9p 會有嚴重的效能低落警告/memory<=8G,所以再找找其他方式)
其中,
直接 mount,
mount -t 9p -o trans=virtio [mount tag] [mount point] -oversion=9p2000.L
而若使用 fstab,則必要先完成以下步驟,
Added to /etc/initramfs-tools/modules:
9p
9pnet
9pnet_virtio
Then:
sudo update-initramfs -u
sudo vim /etc/fstab

Categories: Linux

Tags: , , ,

PHP Code Snippets Powered By : XYZScripts.com