+4

Hệ thống máy ảo với framework Firecracker

Gần đây, mình được yêu cầu tham gia vào quá trình tuyển dụng và onboarding bạn mới. Khi team và dự án phát triển cả về quy mô và độ phức tạp, tài liệu để chuyển giao giữa các dự án cũng càng thêm phức tạp và chỉ hoạt động trong một số hệ điều hành nhất định. Vì vậy, để đảm bảo quá trình tích hợp diễn ra nhanh chóng và có thể sử dụng lại, mình phải đưa ra một hướng mới để tạo ra một môi trường cô lập và đơn giản nhất có thể. Mọi người có thể vào link này để xem rõ hơn

My goal

An isolate enviroment where every member get their own resource and custom preinstall package

Dự án hiện tại ở cty Jobhopin mình đang làm kết hợp nhiều ngôn ngữ như (Rust, Python, ...) và quá trình dựng virtualenv khá dài dòng với phải thử nhiều cách mới chạy được ở máy của bạn mới. Thông thường thì người mới phải mất đến 2-3 tuần để tìm hiểu về các dự án hiện tại và làm việc một cách hiệu quả

Why not use containers image?

Bước đầu, mình khuyến khích team sử dụng Docker và docker-compose để code và debug project nhưng AI team thường có cả thành viên là engineer và data scientist. Team data scientist cảm thấy khó debug trong docker và mất rất nhiều thời gian để các thành viên mới tìm hiểu và sử dụng docker's image. Hơn nữa, tôi muốn tạo một máy ảo (VM) thực sự mà thành viên có quyền truy cập root - mọi người có thể thiết lập sysctls, cài đặt gói mới, tạo quy tắc iptables, cấu hình mạng bằng ip, chạy perf, về cơ bản là bất cứ thứ gì có họ có thể làm như trên personal device của họ.

Why not use virtual machine?

Mình đã thử một số nhà cung cấp VM (Qemu và Vmware) để tạo cho mỗi VM cho mỗi thành viên nhưng quá nhiều vấn đề gặp phải trong quá trình này:

  • Thời gian khởi động VM chậm cộng với kích thước VM quá lớn.
  • Thiếu API và VM backup phải được tạo thủ công mà không có cách nào document và sử dụng lại các step đã khởi tạo được.

Mình muốn các thành viên của team chỉ cần cung cấp thông tin đăng nhập của họ và kích thước VM => submit thông tin là sẽ có ngay một con VM hoàn chỉnh.

Firecracker can start a VM in less than a second with base OCI container

Ban đầu khi mình đọc về việc Firecracker khi nó mới launch, mình nghĩ nó chỉ là một tool cho các cloud vendor sử dụng để cung cấp bảo mật hơn là chạy trên thuần docker, nhưng mình không nghĩ rằng lại có thể sử dụng luôn được để tạo một microVM.

Sau khi đọc một vài thông tin, mình cực kỳ ấn tượng với khả năng tạo VM nhanh chóng và tiện lợi của Firecracker.

The VMM process starts up in around 12ms on AWS EC2 I3.metal instances. Though this time varies, it stays under 60ms. Once the guest VM is configured, it takes a further 125ms to launch the init process in the guest. Firecracker spawns a thread for each VM vCPU to use via the KVM API along with a separate management thread. The memory overhead of each thread (excluding guest memory) is less than 5MB. Ref

Một vài so sánh giữa Firecracker và QEMU

By comparison: Firecracker is purpose-built in Rust for this one task, provides no BIOS, and offers only network, block, keyboard, and serial device support --- with tiny drivers (the serial support is less than 300 lines of code). Ref

Firecracker tích hợp với nhiều tool để quản lý container, làm cho việc áp dụng khá dễ dàng và dễ sử dụng. Tôi chọn sử dụng service Ignite mà lệnh CLI rất giống với docker

With Ignite, you pick an OCI-compliant image (Docker image) that you want to run as a VM, and then just execute ignite run instead of docker run

How to use Firecracker with Ignite

Cài đặt Ignite và khởi động máy ảo mới rất đơn giản, về cơ bản có 3 bước:

Step 1: Kiểm tra xem hệ thống của bạn đã bật ảo hóa KVM chưa và cài đặt Ignite tại đây Installing-guide

$ ignite version
Ignite version: version.Info{Major:"0", Minor:"8", GitVersion:"v0.10.0", GitCommit:"...", GitTreeState:"clean", BuildDate:"...", GoVersion:"...", Compiler:"gc", Platform:"linux/amd64"}
Firecracker version: v0.22.4
Runtime: containerd

Step 2: Tạo một con VM mẫu config.yaml

apiVersion: ignite.weave.works/v1alpha4
kind: VM
metadata:
  name: haiche-vm
spec:
  cpus: 2
  memory: 1GB
  diskSize: 6GB
  image:
    oci: weaveworks/ignite-ubuntu
  ssh: true

Step 3: Khởi động máy chủ VM của bạn dưới 125 mili giây

$ sudo ignite run --config config.yaml

INFO[0001] Created VM with ID "3c5fa9a18682741f" and name "haiche-vm" 

Wolla 🎉 🎉 🎉 bạn đã tạo thành công một máy ảo mới. Để liệt kê các máy ảo đang chạy, hãy nhập:

$ ignite ps
VM ID                   IMAGE                           KERNEL                                  CREATED SIZE    CPUS    MEMORY          STATE   IPS             PORTS   NAME
3c5fa9a18682741f        weaveworks/ignite-ubuntu:latest weaveworks/ignite-kernel:5.10.51        63m ago 4.0 GB  2       1.0 GB          Running 172.17.0.3              haiche-vm

Sau khi máy ảo được khởi động, nó sẽ được cấu hình mạng và có thể truy cập được từ máy chủ thông qua SSH không cần mật khẩu và với quyền sudo

SSH into the VM

Via ignite cli

$ ignite ssh haiche-vm
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 5.10.51 x86_64)
...
[email protected]:~#

Để thoát khỏi SSH, chỉ cần logout khỏi shell như Ctrl+C hoặc gõ exit.

Via ssh cli and rsa key

Thêm public key của bạn vào ~/.ssh/allow_keys trong máy ảo mới được tạo hoặc cập nhập config máy ảo và tạo máy ảo mới với đường dẫn mặc định đến public key

spec:
  ...
  ssh: path/your/id_rsa.pub

Và cuối cùng sử dụng ssh để truy cập vào máy ảo

$ ssh -i path/your/id_rsa [email protected]
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 5.10.51 x86_64)
...
[email protected]:~#

Secure bastion host

Sử dụng bastion host để bảo mật hoàn toàn ssh cho máy chủ VM

~/.ssh/config

Host workstation
    HostName 192.168.1.15
    User haiche
    IdentityFile ~/.ssh/id_rsa

Host haiche-vm
    HostName 172.17.0.3
    ProxyJump workstation
    User root
    IdentityFile ~/.ssh/id_rsa

Thêm cấu hình ở trên vào thư mục ssh của bạn và chạy ssh haiche-vm để truy cập máy chủ VM từ máy client

How I extend container to reduce repeatable setup process

Sau khi tạo VM thành công, chủ yếu là tôi sẽ cài đặt conda và một số tool mà tôi hay xài lên đó. Nhưng tôi không muốn cài đặt nhiều lần conda và tạo môi trường mới mỗi lần tạo lại VM. Đây là các bước để mở rộng image base Ubuntu và sử dụng nó để tạo trải nghiệm VM tốt hơn

Dockerfile

FROM weaveworks/ignite-ubuntu

ENV PATH="/root/miniconda3/bin:${PATH}"
ARG PATH="/root/miniconda3/bin:${PATH}"

RUN apt-get update -qq && \
    apt-get update -y && \
    apt-get install git vim rsync \
        build-essential curl -y

RUN wget \
    https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
    mkdir /root/.conda && \
    bash Miniconda3-latest-Linux-x86_64.sh -b && \
    rm -f Miniconda3-latest-Linux-x86_64.sh

RUN conda create -n haiche python=3.7

SHELL ["conda", "run", "-n", "haiche", "/bin/bash", "-c"]

RUN conda install fastapi

RUN which python && python -c "import fastapi"

RUN conda init bash && echo "source activate haiche" >> ~/.bashrc

miniconda-vm.yaml

apiVersion: ignite.weave.works/v1alpha4
kind: VM
metadata:
  name: haiche-minconda-vm
spec:
  cpus: 2
  memory: 1GB
  diskSize: 6GB
  image:
    oci: haiche/ubuntu-minconda
  ssh: path/your/id_rsa.pub

Đây là một số phần phức tạp, version Ignite hiện tại không hỗ trợ xây dựng image docker từ local. Tôi phải đẩy image vào Docker Hub để import thành công image mới vào Ignite.

Để tạo một máy ảo với Docker image mới

$ sudo ignite run --config miniconda-vm.yaml
...
INFO[0002] Created image with ID "cae0ac317cca74ba" and name "haiche/ubuntu-minconda" 
INFO[0004] Created VM with ID "c1ab652804e664ed" and name "haiche-minconda-vm" 

Nếu bạn sử dụng private registry như ECR, hãy chạy lệnh trên với --runtime docker để pull image từ private registry

Kiểm tra máy ảo với môi trường conda mới

$ ssh -i path/your/id_rsa [email protected]
...
(haiche) [email protected]:~# python
Python 3.7.13 (default, Mar 29 2022, 02:18:16)
[GCC 7.5.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import fastapi
>>> fastapi.__version__
'0.74.1'
>>>

Tôi thích cách tiếp cận sử dụng config file để tạo VM và có thể xem tất cả mọi config ở một nơi. Bây giờ thành viên của team chỉ cần cung cấp config file và public key, tôi chỉ cần gõ 1 câu lệnh là một máy ảo mới với có sẵn tất cả các môi trường cần thiết. Họ có thể vọc vạc và run code ngay lập tức không cần phải học về cách setup môi trường

Cloud supports nested virtualization

Một câu hỏi khác mà mình đã nghĩ đến: “được rồi, đã done các con vm thì sẽ deploy ở đâu trong môi trường Production?”. Điều thú vị khi chạy một máy ảo trên cloud là các cloud instances đã là máy ảo. Việc chạy một máy ảo bên trong một máy ảo được gọi là “nested virtualization” và không phải tất cả các nhà cung cấp dịch vụ cloud đều hỗ trợ - ví dụ: AWS chỉ hỗ trợ nested virtualization trong các phiên bản Bare-metal có giá cao ngất ngưởng.

GCP hỗ trợ nested virtualization nhưng không hỗ trợ mặc định, bạn phải bật tính năng này trong phần tạo instance. DigitalOcean hỗ trợ nested virtualization mặc định ngay cả trên instance rẻ nhất.

Some afterthought

Một số điều vẫn tôi còn thấy lấn cấn với cách tiếp cận tạo VM này:

  • Hiện tại Firecracker không hỗ trợ khôi phục và backup snapshot nhưng sẽ hỗ trợ trong tương lai gần https://github.com/firecracker-microvm/firecracker/issues/1184

  • Không thể dễ dàng nâng cấp base image như docker pull. Tôi đã giải quyết vấn đề này bằng cách tạo bản copy mới, nhưng điều đó khá chậm và thực sự không hiệu quả. Có một số giải pháp mà tôi tìm được và sẽ thử sau lại sau Device mapper to manage firecracker images

  • Tôi không biết liệu có thể chạy các ứng dụng đồ họa trong Firecracker hay không

  • Firecracker với Kubernetes là một trend mới nhưng tôi không thấy hấp dẫn vì việc sử dụng Pod để nhóm các vùng chứa đã nhanh chóng và an toàn. Đây là một thread hữu ích thảo luận về lý do Tại sao chúng chưa tương thích với nhau

Dưới đây là một số links tôi thấy hữu ích khi nghiên cứu về Firecracker:


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.