mirror of https://github.com/cirruslabs/tart.git
1 line
79 KiB
JSON
1 line
79 KiB
JSON
{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"faq/","title":"Frequently Asked Questions","text":""},{"location":"faq/#headless-machines","title":"Headless machines","text":"<p>Starting from macOS 15 (Sequoia), there's an undocumented requirement from Virtualization.Framework (which Tart uses) to have an unlocked <code>login.keychain</code> available at the times when running a VM.</p> <p>Without an existing and unlocked <code>login.keychain</code>, the VM won't start with errors like:</p> <ul> <li><code>SecKeyCreateRandomKey_ios failed</code></li> <li><code>Failed to generate keypair</code></li> <li><code>Interaction is not allowed with the Security Server</code></li> </ul> <p>Below you'll find a couple of workarounds for this behavior.</p>"},{"location":"faq/#log-in-via-gui-at-least-once","title":"Log in via GUI at least once","text":"<p>Connect to the headless machine via Screen Sharing and log in to a Mac user account. If you haven't done already, you can enable Screen Sharing via the terminal.</p> <p>Logging in graphically will automatically create the <code>login.keychain</code>. Afterward, you have two options:</p> <ul> <li>configure automatic log in to a Mac user account<ul> <li>this will maintain a running user session (GUI) even after the machine reboots</li> <li>moreover, you can still lock the screen (either manually or automatically), however, the security benefit of this is questionable</li> </ul> </li> <li>use <code>security unlock-keychain login.keychain</code> to unlock the login keychain via the terminal<ul> <li>this command also supports the <code>-p</code> command-line argument, which allows you to supply a password and unlock non-interactively</li> </ul> </li> </ul>"},{"location":"faq/#create-and-unlock-the-login-keychain-via-the-terminal","title":"Create and unlock the login keychain via the terminal","text":"<p>Compared to the previous approach, this one is fully automated, but might stop working at some point in the future:</p> <pre><code>security create-keychain -p '' login.keychain\nsecurity unlock-keychain -p '' login.keychain\nsecurity login-keychain -s login.keychain\n</code></pre> <p>Note that this will create a <code>login.keychain</code> with an empty password. Consider supplying a different value to <code>-p</code> or omitting the <code>-p</code> to enter the password interactively. </p>"},{"location":"faq/#troubleshooting-crashes","title":"Troubleshooting crashes","text":"<p>If you experience a crash or encounter another error while using the tart executable, you can collect debug information to assist with troubleshooting. Run the following command in a separate terminal window to gather logs from the Tart process and the macOS Virtualization subsystem:</p> <pre><code>log stream --predicate='process==\"tart\" OR process CONTAINS \"Virtualization\"' > tart.log\n</code></pre> <p>While the events are being streamed, attempt to reproduce the issue. Once the issue is reproduced, stop the streaming by pressing Ctrl+C. Then, attach the tart.log file to your report.</p>"},{"location":"faq/#vm-location-on-disk","title":"VM location on disk","text":"<p>Tart stores all its files in <code>~/.tart/</code> directory. Local images that you can run are stored in <code>~/.tart/vms/</code>. Remote images are pulled into <code>~/.tart/cache/OCIs/</code>.</p>"},{"location":"faq/#nested-virtualization-support","title":"Nested virtualization support?","text":"<p>Tart is limited by functionality of Apple's <code>Virtualization.Framework</code>. At the moment <code>Virtualization.Framework</code> supports nested virtualization only on M3 or M4 chips running macOS 15 (Sequoia) or later and only for Linux VMs. By default, it is disabled, but can be enabled by passing the <code>--nested</code> flag to <code>tart run</code>.</p>"},{"location":"faq/#connecting-to-a-service-running-on-host","title":"Connecting to a service running on host","text":"<p>To connect from within a virtual machine to a service running on the host machine please first make sure that the service is bound to <code>0.0.0.0</code>.</p> <p>Then from within a virtual machine you can access the service using the router's IP address that you can get either from <code>Preferences -> Network</code> or by running the following command in the Terminal:</p> <pre><code>netstat -nr | awk '/default/{print $2; exit}'\n</code></pre> <p>Note: that accessing host is only possible with the default NAT network. If you are running your virtual machines with Softnet (via <code>tart run --net-softnet <VM NAME>)</code>, then the network isolation is stricter and it's not possible to access the host.</p>"},{"location":"faq/#avoiding-the-local-network-permission-pop-up","title":"Avoiding the \"Local Network\" permission pop-up","text":"<p>Starting from macOS 15 (Sequoia), a GUI \"Local Network\" permission pop-up might appear when Tart is used by another tool that needs to connect to a VM over a private IPv4 network, for example Packer.</p> <p>This is not caused by Tart itself, but by the host-side process that needs network access into the guest.</p> <p>If you need a non-interactive workaround, you can configure the local network privacy preferences on the host so that all RFC 1918 address ranges that Tart VMs commonly use are excluded from the prompt:</p> <pre><code>sudo defaults write com.apple.network.local-network AllowedEthernetLocalNetworkAddresses -array \"10.0.0.0/8\" \"172.16.0.0/12\" \"192.168.0.0/16\"\nsudo defaults write com.apple.network.local-network AllowedWiFiLocalNetworkAddresses -array \"10.0.0.0/8\" \"172.16.0.0/12\" \"192.168.0.0/16\"\n</code></pre> <p>After applying these settings, reboot the host.</p>"},{"location":"faq/#changing-the-default-nat-subnet","title":"Changing the default NAT subnet","text":"<p>To change the default network to <code>192.168.77.1</code>:</p> <pre><code>sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist Shared_Net_Address -string 192.168.77.1\n</code></pre> <p>Note that even through a network would normally be specified as <code>192.168.77.0</code>, the vmnet framework seems to treat this as a starting address too and refuses to pick up such network-like values.</p> <p>The default subnet mask <code>255.255.255.0</code> should suffice for most use-cases, however, you can also change it to <code>255.255.0.0</code>, for example:</p> <pre><code>sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.vmnet.plist Shared_Net_Mask -string 255.255.0.0\n</code></pre>"},{"location":"faq/#changing-the-default-dhcp-lease-time","title":"Changing the default DHCP lease time","text":"<p>By default, the built-in macOS DHCP server allocates IP-addresses to the VMs for the duration of 86,400 seconds (one day), which may easily cause DHCP exhaustion if you run more than ~253 VMs per day, or in other words, more than one VM every ~6 minutes.</p> <p>This issue is worked around automatically when using Softnet, however, if you don't use or can't use it, the following command will reduce the lease time from the default 86,400 seconds (one day) to 600 seconds (10 minutes):</p> <pre><code>sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.InternetSharing.default.plist bootpd -dict DHCPLeaseTimeSecs -int 600\n</code></pre> <p>This tweak persists across reboots, so normally you'll only need to do it once per new host.</p> <p>If that doesn't help after starting a new VM, it's possible that the <code>/var/db/dhcpd_leases</code> file is already overfilled with 86,400-second leases. You can remove it with the following command and try starting a new VM again:</p> <pre><code>sudo rm /var/db/dhcpd_leases\n</code></pre> <p>And no worries, this file will be re-created on the next <code>tart run</code>.</p>"},{"location":"faq/#unsupported-dhcp-client-identifiers","title":"Unsupported DHCP client identifiers","text":"<p>Due to the limitations of the macOS built-in DHCP server, <code>tart ip</code> is unable to correctly report the IP addresses for VMs using DHCP client identifiers that are not based on VMs link-layer addresses (MAC addresses).</p> <p>By default, when no <code>--resolver=arp</code> is specified, <code>tart ip</code> reads the <code>/var/db/dhcpd_leases</code> file and tries to find the freshest entry that matches the VM's MAC address (based on the <code>hw_address</code> field).</p> <p>However, things starts to break when the VM uses a DUID-EN identifier, for example. One of the notorious examples of this being Ubuntu, using this type of identifier by default on latest versions. This results in the <code>/var/db/dhcpd_leases</code> entry for Ubuntu appearing as follows:</p> <pre><code>{\n name=ubuntu\n ip_address=192.168.64.3\n hw_address=ff,f1:f5:dd:7f:0:2:0:0:ab:11:cb:fb:30:b0:97:b6:3a:67\n identifier=ff,f1:f5:dd:7f:0:2:0:0:ab:11:cb:fb:30:b0:97:b6:3a:67\n lease=0x678e2ce7\n}\n</code></pre> <p>Because the macOS built-in DHCP server overwrites the <code>hw_address</code> with the <code>identifier</code>, it leaves no information about the VM's MAC address to the <code>tart ip</code>.</p> <p>To avoid this issue, make sure that your VM only sends a DHCP client identifier (option 61) with link-layer address (MAC address) or that it doesn't send this option at all.</p> <p>For the aforementioned Ubuntu, the solution is outlined in the section How to integrate with Windows DHCP Server of Canonical Netplan's documentation:</p> <pre><code>network:\n version: 2\n ethernets:\n enp3s0:\n dhcp4: yes\n dhcp-identifier: mac\n</code></pre>"},{"location":"faq/#resolving-the-vms-ip-when-using-bridged-networking","title":"Resolving the VM's IP when using bridged networking","text":"<p>When running <code>tart run</code> with <code>--net-bridged</code>, you need to invoke <code>tart ip</code> differently, because the macOS built-in DHCP server won't have any information about the VM's IP-address:</p> <pre><code>tart ip --resolver=arp <VM>\n</code></pre> <p>This causes the <code>tart ip</code> to consult the host's ARP table instead of the <code>/var/db/dhcpd_leases</code> file.</p> <p>Note that this method of resolving the IP heavily relies on the level of VM's activity on the network, namely, exchanging ARP requests between the guest and the host.</p> <p>This is normally not an issue for macOS VMs, but on Linux VMs you might need to install Samba, which includes a NetBIOS name server and exhibits the same behavior as macOS, resulting in the population of the ARP table of the host OS:</p> <pre><code>sudo apt-get install samba\n</code></pre>"},{"location":"faq/#running-loginclonepullpush-commands-over-ssh","title":"Running login/clone/pull/push commands over SSH","text":"<p>When invoking the Tart in an SSH session, you might get error like this:</p> <p>Keychain returned unsuccessful status -25308</p> <p>...or this:</p> <p>Keychain failed to update item: User interaction is not allowed.</p> <p>This is because Tart uses Keychain to store and retrieve OCI registry credentials by default, but Keychain is only automatically/semi-automatically unlocked in GUI sessions.</p> <p>To unlock the Keychain in an SSH session, run the following command, which will ask for your user's password:</p> <pre><code>security unlock-keychain login.keychain\n</code></pre> <p>This command also supports the <code>-p</code> command-line argument that allows you to supply a password and unlock non-interactively, which is great for scripts.</p> <p>Alternatively, you can pass the credentials via the environment variables, see Registry Authorization for more details on how to do that.</p>"},{"location":"faq/#how-is-tart-different-from-anka","title":"How is Tart different from Anka?","text":"<p>Under the hood Tart is using the same technology as Anka 3.0 so there should be no real difference in performance or features supported. If there is some feature missing please don't hesitate to create a feature request.</p> <p>Instead of Anka Registry, Tart can work with any OCI-compatible container registry. This provides a much more consistent and scalable experience for distributing virtual machines.</p> <p>Tart does have an analogue of Anka Controller for managing VMs across a cluster of Mac hosts called Orchard.</p>"},{"location":"faq/#automatic-pruning","title":"Automatic pruning","text":"<p><code>tart pull</code> and <code>tart clone</code> commands check the remaining space available on the volume associated with <code>TART_HOME</code> directory (defaults to <code>~/.tart</code>) before pulling or cloning anything.</p> <p>In case there's not enough space to fit the newly pulled or cloned VM image, Tart will remove the least recently accessed VMs from OCI cache and <code>.ipsw</code> files from IPSW cache until enough free space is available.</p> <p>The <code>tart clone</code> command limits this automatic pruning to 100 GB by default to avoid removing too many cached items. You can change this limit with the <code>--prune-limit</code> option (in gigabytes).</p> <p>To disable this functionality, set the <code>TART_NO_AUTO_PRUNE</code> environment variable either globally:</p> <pre><code>export TART_NO_AUTO_PRUNE=\n</code></pre> <p>...or per <code>tart pull</code> and <code>tart clone</code> invocation as follows:</p> <pre><code>TART_NO_AUTO_PRUNE= tart pull ...\n</code></pre>"},{"location":"faq/#disk-resizing","title":"Disk resizing","text":"<p>Disk resizing works on most cloud-ready Linux distributions out-of-the box (e.g. Ubuntu Cloud Images have the <code>cloud-initramfs-growroot</code> package installed that runs on boot) and on the rest of the distributions by running the <code>growpart</code> or <code>resize2fs</code> commands.</p> <p>For macOS, however, things are a bit more complicated, and you generally have two options: automated and manual resizing.</p> <p>For the automated option, you can use Packer with the Packer builder for Tart VMs. The latter has two has configuration directives related to the disk resizing behavior:</p> <ul> <li><code>disk_size_gb</code> \u2014 controls the target disk size in gigabytes</li> <li><code>recovery_partition</code> \u2014 controls what to do with the recovery partition when resizing the disk<ul> <li>you can either keep, delete or relocate it to the end of the disk</li> </ul> </li> </ul> <p>For the manual approach, you have to remove the recovery partition first, repair the disk and the resize the APFS container.</p> <p>To do this, first we'll need to identify the primary disk and the APFS containers by running the command below from within a VM:</p> <pre><code>diskutil list physical\n</code></pre> <p>For example, the output might look like this:</p> <pre><code>/dev/disk0 (internal, physical):\n #: TYPE NAME SIZE IDENTIFIER\n 0: GUID_partition_scheme *100.0 GB disk0\n 1: Apple_APFS_ISC Container disk1 524.3 MB disk0s1\n 2: Apple_APFS Container disk3 44.1 GB disk0s2\n 3: Apple_APFS_Recovery Container disk2 5.4 GB disk0s3\n (free space) 50.0 GB -\n</code></pre> <p>In the output, you'll normally see:</p> <ul> <li>a single physical disk (<code>disk0</code>)</li> <li>APFS container with the system partition which we're going to resize (<code>disk0s2</code>)</li> <li>APFS container with the recovery partition which we're going to delete (<code>disk0s3</code>)</li> <li><code>(free space)</code> which we'll put to use</li> </ul> <p>To proceed, boot the VM in recovery mode using <code>tart run --recovery</code> and choose the \"Options\" item:</p> <p></p> <p>When the recovery OS boots, open the Terminal app:</p> <p></p> <p>In Terminal app, invoke the command below to remove the recovery partition:</p> <pre><code>diskutil eraseVolume free free disk0s3\n</code></pre> <p>Now, repair the disk:</p> <pre><code>yes | diskutil repairDisk disk0\n</code></pre> <p>Finally, resize the system APFS container to take all the remaining space:</p> <pre><code>diskutil apfs resizeContainer disk0s2 0\n</code></pre> <p>Now, you can shut down and <code>tart run</code> as you'd normally do.</p>"},{"location":"licensing/","title":"Licensing and Support","text":"<p>Both Tart Virtualization and Orchard Orchestration are licensed under Fair Source License. Usage on personal computers including personal workstations is royalty-free, but organizations that exceed a certain number of server installations (100 CPU cores for Tart and/or 4 hosts for Orchard) will be required to obtain a paid license.</p> Host CPU Core usage <p>The virtual CPU cores of Tart VMs are not tied to specific physical cores of the host CPU. Instead, for optimal performance Tart VMs will automatically try to balance compute between all available cores of the host CPU. As a result, all performance and energy-efficient cores of the host CPU are always counted towards the license usage.</p>"},{"location":"licensing/#license-tiers","title":"License Tiers","text":"<p>By default, when no license is purchased, it is assumed that an organization is using a Free Tier license. You can find the Free Tier license text in Tart and Orchard repositories.</p> <p>Free Tier license has a 100 CPU core limit for Tart and 4 Orchard Workers limit for Orchard.</p> Usage Scenarios Examples <p>Here are a few examples that fit into the free tier:</p> <ul> <li>Using Tart on 12 Mac Minis with 8 CPUs each running up to 24 VMs in parallel.</li> <li>Creating an Orchard cluster of 4 Mac Studio workers with 24 CPUs each.</li> </ul> <p>Here are a few examples that do not fit into the free tier:</p> <ul> <li>Using Tart on 13 Mac Minis with 8 CPUs each.</li> <li>Creating an Orchard cluster of 5 Mac Minis workers with 8 CPUs each.</li> </ul>"},{"location":"licensing/#gold-tier","title":"Gold Tier","text":"<p>If an organization wishes to exceed the limits of the Free Tier license, a purchase of the Gold Tier License is required, which costs $12,000 per year.</p> <p>Gold Tier license has a 500 CPU core limit for Tart and 20 Orchard Workers limit for Orchard.</p>"},{"location":"licensing/#platinum-tier","title":"Platinum Tier","text":"<p>If an organization wishes to exceed the limits of the Gold Tier license, a purchase of the Platinum Tier License is required, which costs $36,000 per year.</p> <p>Platinum Tier license has a 3,000 CPU core limit for Tart and 200 Orchard Workers limit for Orchard.</p>"},{"location":"licensing/#diamond-tier","title":"Diamond Tier","text":"<p>For organizations that wish to exceed the limits of the Platinum Tier license, a purchase of a custom Diamond Tier License is required, which costs $12 per CPU core per year and gives the ability to run unlimited Orchard Workers.</p>"},{"location":"licensing/#get-the-license","title":"Get the license","text":"<p>If your organization is interested in purchasing one of the license tiers, please email licensing@cirruslabs.org.</p> <p>You can see a template of a license subscription agreement here.</p> <p>Running on AWS?</p> <p>There are official AMIs for EC2 Mac Instances with preconfigured Tart installation that is optimized to work within AWS infrastructure.</p> <p>Additionally, there is a ECR Pulic Gallery mirror of all the Tart VM images managed by us.</p>"},{"location":"licensing/#general-support","title":"General Support","text":"<p>The best way to ask general questions about particular use cases is to email our support team at support@cirruslabs.org. Our support team is trying our best to respond ASAP, but there is no guarantee on a response time unless your organization has a paid license subscription which includes Priority Support.</p> <p>If you have a feature request or noticed lack of some documentation please feel free to create a GitHub issue. Our support team will answer it by replying to the issue or by updating the documentation.</p>"},{"location":"licensing/#priority-support","title":"Priority Support","text":"<p>In addition to the general support we provide a Priority Support with guaranteed response times included in all the paid license tiers.</p> Severity Support Impact First Response Time SLA Hours How to Submit 1 Emergency (service is unavailable or completely unusable). 30 minutes 24x7 Please use urgent email address. 2 Highly Degraded (Important features unavailable or extremely slow; No acceptable workaround). 4 hours 24x5 Please use priority email address. 3 Medium Impact. 8 hours 24x5 Please use priority email address. 4 Low Impact. 24 hours 24x5 Please use regular support email address. Make sure to send the email from your corporate email. <p><code>24x5</code> means period of time from 9AM on Monday till 5PM on Friday in EST timezone.</p> Support Impact Definitions <ul> <li>Severity 1 - Your installation of Orchard is unavailable or completely unusable. An urgent issue can be filed and our On-Call Support Engineer will respond within 30 minutes. Example: Orchard Controller is showing 502 errors for all users.</li> <li>Severity 2 - Orchard installation is Highly Degraded. Significant Business Impact. Important features are unavailable or extremely slowed, with no acceptable workaround.</li> <li>Severity 3 - Something is preventing normal service operation. Some Business Impact. Important features of Tart or Orchard are unavailable or somewhat slowed, but a workaround is available.</li> <li>Severity 4 - Questions or Clarifications around features or documentation. Minimal or no Business Impact. Information, an enhancement, or documentation clarification is requested, but there is no impact on the operation of Tart and/or Orchard.</li> </ul> <p>How to submit a priority or an urgent issue</p> <p>Once your organization obtains a license, members of your organization will get access to separate support emails specified in your subscription contract.</p>"},{"location":"quick-start/","title":"Quick Start","text":"<p>Try running a Tart VM on your Apple Silicon device running macOS 13.0 (Ventura) or later (will download a 25 GB image):</p> <pre><code>brew install cirruslabs/cli/tart\ntart clone ghcr.io/cirruslabs/macos-tahoe-base:latest tahoe-base\ntart run tahoe-base\n</code></pre> Manual installation from a release archive <p>It's also possible to manually install <code>tart</code> binary from the latest released archive:</p> <pre><code>curl -LO https://github.com/cirruslabs/tart/releases/latest/download/tart.tar.gz\ntar -xzvf tart.tar.gz\n./tart.app/Contents/MacOS/tart clone ghcr.io/cirruslabs/macos-tahoe-base:latest tahoe-base\n./tart.app/Contents/MacOS/tart run tahoe-base\n</code></pre> <p>Please note that <code>./tart.app/Contents/MacOS/tart</code> binary is required to be used in order to trick macOS to pick <code>tart.app/Contents/embedded.provisionprofile</code> for elevated privileges that Tart needs.</p> <p> </p>"},{"location":"quick-start/#vm-images","title":"VM images","text":"<p>The following macOS images are currently available:</p> <ul> <li>macOS 26 (Tahoe)<ul> <li><code>ghcr.io/cirruslabs/macos-tahoe-vanilla:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-tahoe-base:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-tahoe-xcode:latest</code></li> </ul> </li> <li>macOS 15 (Sequoia)<ul> <li><code>ghcr.io/cirruslabs/macos-sequoia-vanilla:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-sequoia-base:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-sequoia-xcode:latest</code></li> </ul> </li> <li>macOS 14 (Sonoma)<ul> <li><code>ghcr.io/cirruslabs/macos-sonoma-vanilla:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-sonoma-base:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-sonoma-xcode:latest</code></li> </ul> </li> <li>macOS 13 (Ventura)<ul> <li><code>ghcr.io/cirruslabs/macos-ventura-vanilla:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-ventura-base:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-ventura-xcode:latest</code></li> </ul> </li> <li>macOS 12 (Monterey)<ul> <li><code>ghcr.io/cirruslabs/macos-monterey-vanilla:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-monterey-base:latest</code></li> <li><code>ghcr.io/cirruslabs/macos-monterey-xcode:latest</code></li> </ul> </li> </ul> <p>There's also a full list of images in which you can discovery specific tags (e.g. <code>ghcr.io/cirruslabs/macos-monterey-xcode:15</code>) and macOS-specific Packer templates that were used to generate these images.</p> <p>For, Linux the options are as follows:</p> <ul> <li>Ubuntu<ul> <li><code>ghcr.io/cirruslabs/ubuntu:latest</code></li> </ul> </li> <li>Debian<ul> <li><code>ghcr.io/cirruslabs/debian:latest</code></li> </ul> </li> <li>Fedora<ul> <li><code>ghcr.io/cirruslabs/fedora:latest</code></li> </ul> </li> </ul> <p>Note that these Linux images have a minimal disk size of 20 GB, and you might want to resize them right after cloning:</p> <pre><code>tart clone ghcr.io/cirruslabs/ubuntu:latest ubuntu\ntart set ubuntu --disk-size 50\ntart run ubuntu\n</code></pre> <p>These Linux images can be ran natively on Vetu, our virtualization solution for Linux, assuming that Vetu itself is running on an <code>arm64</code> machine.</p> <p>Similarly to macOS, there's also a full list of images in which you can discovery specific tags (e.g. <code>ghcr.io/cirruslabs/ubuntu:22.04</code>) and Linux-specific Packer templates that were used to generate these images.</p> <p>All images above use the following credentials:</p> <ul> <li>Username: <code>admin</code></li> <li>Password: <code>admin</code></li> </ul> <p>These credentials work both for logging in via GUI, console (Linux) and SSH.</p>"},{"location":"quick-start/#ssh-access","title":"SSH access","text":"<p>If the guest VM is running and configured to accept incoming SSH connections you can conveniently connect to it like so:</p> <pre><code>ssh admin@$(tart ip tahoe-base)\n</code></pre> <p>Running scripts inside Tart virtual machines</p> <p>We recommend using Cirrus CLI to run scripts and/or retrieve artifacts from within Tart virtual machines. Alternatively, you can use plain ssh connection and <code>tart ip</code> command:</p> <pre><code>brew install cirruslabs/cli/sshpass\nsshpass -p admin ssh -o \"StrictHostKeyChecking no\" -o \"UserKnownHostsFile=/dev/null\" admin@$(tart ip tahoe-base) \"uname -a\"\nsshpass -p admin ssh -o \"StrictHostKeyChecking no\" -o \"UserKnownHostsFile=/dev/null\" admin@$(tart ip tahoe-base) < script.sh\n</code></pre>"},{"location":"quick-start/#creating-vm-images","title":"Creating VM images","text":"<p>Tart supports macOS and Linux virtual machines. All commands like <code>run</code> and <code>pull</code> work the same way regardless of the underlying OS a particular VM image has. The only difference is how such VM images are created. Please check sections below for macOS and Linux instructions.</p>"},{"location":"quick-start/#creating-a-macos-vm-image-from-scratch","title":"Creating a macOS VM image from scratch","text":"<p>Tart can create VMs from <code>*.ipsw</code> files. You can download a specific <code>*.ipsw</code> file here or you can use <code>latest</code> instead of a path to <code>*.ipsw</code> to download the latest available version:</p> <pre><code>tart create --from-ipsw=latest tahoe-vanilla\ntart run tahoe-vanilla\n</code></pre> <p>After the initial booting of the VM, you'll need to manually go through the macOS installation process. As a convention we recommend creating an <code>admin</code> user with an <code>admin</code> password. After the regular installation please do some additional modifications in the VM:</p> <ol> <li>Enable Auto-Login. Users & Groups -> Login Options -> Automatic login -> admin.</li> <li>Allow SSH. Sharing -> Remote Login</li> <li>Disable Lock Screen. Preferences -> Lock Screen -> disable \"Require Password\" after 5.</li> <li>Disable Screen Saver.</li> <li>Run <code>sudo visudo</code> in Terminal, find <code>%admin ALL=(ALL) ALL</code> add <code>admin ALL=(ALL) NOPASSWD: ALL</code> to allow sudo without a password.</li> </ol>"},{"location":"quick-start/#creating-a-linux-vm-image-from-scratch","title":"Creating a Linux VM image from scratch","text":"<p>Linux VMs are supported on hosts running macOS 13.0 (Ventura) or newer.</p> <pre><code># Create a bare VM\ntart create --linux ubuntu\n\n# Install Ubuntu\ntart run --disk focal-desktop-arm64.iso ubuntu\n\n# Run VM\ntart run ubuntu\n</code></pre> <p>After the initial setup please make sure your VM can be SSH-ed into by running the following commands inside your VM:</p> <pre><code>sudo apt update\nsudo apt install -y openssh-server\nsudo ufw allow ssh\n</code></pre>"},{"location":"quick-start/#configuring-a-vm","title":"Configuring a VM","text":"<p>By default, a Tart VM uses 2 CPUs and 4 GB of memory with a <code>1024x768</code> display. This can be changed after VM creation with <code>tart set</code> command. Please refer to <code>tart set --help</code> for additional details.</p>"},{"location":"quick-start/#mounting-directories","title":"Mounting directories","text":"<p>To mount a directory, run the VM with the <code>--dir</code> argument:</p> <pre><code>tart run --dir=project:~/src/project vm\n</code></pre> <p>Here, the <code>project</code> specifies a mount name, whereas the <code>~/src/project</code> is a path to the host's directory to expose to the VM.</p> <p>It is also possible to mount directories in read-only mode by adding a third parameter, <code>ro</code>:</p> <pre><code>tart run --dir=project:~/src/project:ro vm\n</code></pre> <p>To mount multiple directories, repeat the <code>--dir</code> argument for each directory:</p> <pre><code>tart run --dir=www1:~/project1/www --dir=www2:~/project2/www\n</code></pre> <p>Note that the first parameter in each <code>--dir</code> argument must be unique, otherwise only the last <code>--dir</code> argument using that name will be used.</p> <p>Note: to use the directory mounting feature, the host needs to run macOS 13.0 (Ventura) or newer.</p>"},{"location":"quick-start/#accessing-mounted-directories-in-macos-guests","title":"Accessing mounted directories in macOS guests","text":"<p>All shared directories are automatically mounted to <code>/Volumes/My Shared Files</code> directory.</p> <p>The directory we've mounted above will be accessible from the <code>/Volumes/My Shared Files/project</code> path inside a guest VM.</p> <p>Note: to use the directory mounting feature, the guest VM needs to run macOS 13.0 (Ventura) or newer.</p> Changing mount location <p>It is possible to remount the directories after a virtual machine is started by running the following commands:</p> <pre><code>sudo umount \"/Volumes/My Shared Files\"\nmkdir ~/workspace\nmount_virtiofs com.apple.virtio-fs.automount ~/workspace\n</code></pre> <p>After running the above commands the direcory will be available at <code>~/workspace/project</code></p>"},{"location":"quick-start/#accessing-mounted-directories-in-linux-guests","title":"Accessing mounted directories in Linux guests","text":"<p>To be able to access the shared directories from the Linux guest, you need to manually mount the virtual filesystem first:</p> <pre><code>sudo mkdir /mnt/shared\nsudo mount -t virtiofs com.apple.virtio-fs.automount /mnt/shared\n</code></pre> <p>The directory we've mounted above will be accessible from the <code>/mnt/shared/project</code> path inside a guest VM.</p> Auto-mount at boot time <p>To automatically mount this directory at boot time, add the following line to the <code>/etc/fstab</code> file:</p> <pre><code>com.apple.virtio-fs.automount /mnt/shared virtiofs rw,relatime 0 0\n</code></pre>"},{"location":"quick-start/#working-with-a-remote-oci-container-registry","title":"Working with a Remote OCI Container Registry","text":"<p>Tart supports interacting with Open Container Initiative (OCI) registries, but only runs images created and pushed by Tart. This means images created for container engines, like Docker, can't be pulled. Instead, create a custom image as documented above.</p> <p>For example, let's say you want to push/pull images to an OCI registry hosted at <code>https://acme.io/</code>.</p>"},{"location":"quick-start/#registry-authorization","title":"Registry Authorization","text":"<p>First, you need to login to <code>acme.io</code> with the <code>tart login</code> command:</p> <pre><code>tart login acme.io\n</code></pre> <p>If you login to your registry with OAuth, you may need to create an access token to use as the password. Credentials are securely stored in Keychain.</p> <p>In addition, Tart supports Docker credential helpers if defined in <code>~/.docker/config.json</code>.</p> <p>Finally, <code>TART_REGISTRY_USERNAME</code> and <code>TART_REGISTRY_PASSWORD</code> environment variables allow to override authorization for all registries, which might be useful for integrating with your CI's secret management.</p> <p>You can also set the <code>TART_REGISTRY_HOSTNAME</code> environment variable to apply these overrides only to a specific host.</p>"},{"location":"quick-start/#pushing-a-local-image","title":"Pushing a Local Image","text":"<p>Once credentials are saved for <code>acme.io</code>, run the following command to push a local images remotely with two tags:</p> <pre><code>tart push my-local-vm-name acme.io/remoteorg/name:latest acme.io/remoteorg/name:v1.0.0\n</code></pre>"},{"location":"quick-start/#pulling-a-remote-image","title":"Pulling a Remote Image","text":"<p>You can either pull an image:</p> <pre><code>tart pull acme.io/remoteorg/name:latest\n</code></pre> <p>or create a VM from a remote image:</p> <pre><code>tart clone acme.io/remoteorg/name:latest my-local-vm-name\n</code></pre> <p>If the specified image is not already present, this invocation calls the <code>tart pull</code> implicitly before cloning.</p>"},{"location":"blog/","title":"Blog","text":""},{"location":"integrations/buildkite/","title":"Buildkite","text":"<p>It is possible to run Buildkite pipeline steps in isolated ephemeral Tart Virtual Machines with the help of Tart Buildkite Plugin:</p> <p></p>"},{"location":"integrations/buildkite/#configuration","title":"Configuration","text":"<p>The most basic configuration looks like this:</p> <pre><code>steps:\n- command: uname -a\n plugins:\n - cirruslabs/tart#main:\n image: ghcr.io/cirruslabs/macos-tahoe-base:latest\n</code></pre> <p>This will run <code>uname -r</code> in a macOS Tart VM cloned from <code>ghcr.io/cirruslabs/macos-tahoe-base:latest</code>.</p> <p>See plugin's Configuration section for the full list of available options.</p>"},{"location":"integrations/cirrus-cli/","title":"Cirrus CLI","text":"<p>Tart itself is only responsible for managing virtual machines, but we've built Tart support into a tool called Cirrus CLI also developed by Cirrus Labs. Cirrus CLI is a command line tool with one configuration format to execute common CI steps (run a script, cache a folder, etc.) locally or in any CI system. We built Cirrus CLI to solve \"But it works on my machine!\" problem.</p> <p>Here is an example of a <code>.cirrus.yml</code> configuration file which will start a Tart VM, will copy over working directory and will run scripts and other instructions inside the virtual machine:</p> <pre><code>task:\n name: hello\n macos_instance:\n # can be a remote or a local virtual machine\n image: ghcr.io/cirruslabs/macos-tahoe-base:latest\n hello_script:\n - echo \"Hello from within a Tart VM!\"\n - echo \"Here is my CPU info:\"\n - sysctl -n machdep.cpu.brand_string\n - sleep 15\n</code></pre> <p>Put the above <code>.cirrus.yml</code> file in the root of your repository and run it with the following command:</p> <pre><code>brew install cirruslabs/cli/cirrus\ncirrus run\n</code></pre> <p></p> <p>Cirrus CI already leverages Tart to power its macOS cloud infrastructure. The <code>.cirrus.yml</code> config from above will just work in Cirrus CI and your tasks will be executed inside Tart VMs in our cloud.</p> <p>Note: Cirrus CI only allows images managed and regularly updated by us.</p>"},{"location":"integrations/cirrus-cli/#retrieving-artifacts-from-within-tart-vms","title":"Retrieving artifacts from within Tart VMs","text":"<p>In many cases there is a need to retrieve particular files or a folder from within a Tart virtual machine. For example, the below <code>.cirrus.yml</code> configuration defines a single task that builds a <code>tart</code> binary and exposes it via <code>artifacts</code> instruction:</p> <pre><code>task:\n name: Build\n macos_instance:\n image: ghcr.io/cirruslabs/macos-tahoe-xcode:latest\n build_script: swift build --product tart\n binary_artifacts:\n path: .build/debug/tart\n</code></pre> <p>Running Cirrus CLI with <code>--artifacts-dir</code> will write defined <code>artifacts</code> to the provided local directory on the host:</p> <pre><code>cirrus run --artifacts-dir artifacts\n</code></pre> <p>Note that all retrieved artifacts will be prefixed with the associated task name and <code>artifacts</code> instruction name. For the example above, <code>tart</code> binary will be saved to <code>$PWD/artifacts/Build/binary/.build/debug/tart</code>.</p>"},{"location":"integrations/gitlab-runner/","title":"GitLab Runner Executor","text":"<p>It is possible to run GitLab jobs in isolated ephemeral Tart Virtual Machines via Tart Executor. Tart Executor utilizes custom executor feature of GitLab Runner.</p>"},{"location":"integrations/gitlab-runner/#basic-configuration","title":"Basic Configuration","text":"<p>Configuring Tart Executor for GitLab Runner is as simple as installing <code>gitlab-tart-executor</code> binary from Homebrew:</p> <pre><code>brew install cirruslabs/cli/gitlab-tart-executor\n</code></pre> <p>And updating configuration of your self-hosted GitLab Runner to use <code>gitlab-tart-executor</code> binary:</p> <pre><code>concurrent = 2\n\n[[runners]]\n # ...\n executor = \"custom\"\n builds_dir = \"/Users/admin/builds\" # directory inside the VM\n cache_dir = \"/Users/admin/cache\"\n [runners.feature_flags]\n FF_RESOLVE_FULL_TLS_CHAIN = false\n [runners.custom]\n prepare_exec = \"gitlab-tart-executor\"\n prepare_args = [\"prepare\"]\n run_exec = \"gitlab-tart-executor\"\n run_args = [\"run\"]\n cleanup_exec = \"gitlab-tart-executor\"\n cleanup_args = [\"cleanup\"]\n</code></pre> <p>Now you can use Tart Images in your <code>.gitlab-ci.yml</code>:</p> <pre><code># You can use any remote Tart Image.\n# Tart Executor will pull it from the registry and use it for creating ephemeral VMs.\nimage: ghcr.io/cirruslabs/macos-tahoe-base:latest\n\ntest:\n tags:\n - tart-installed # in case you tagged runners with Tart Executor installed\n script:\n - uname -a\n</code></pre> <p>For more advanced configuration please refer to GitLab Tart Executor repository.</p>"},{"location":"integrations/packer/","title":"Automating VM image building with Packer","text":"<p>Please refer to Tart Packer Plugin repository for setup instructions. Here is an example of a template to build a local image based of a remote image:</p> <pre><code>packer {\n required_plugins {\n tart = {\n version = \">= 0.5.3\"\n source = \"github.com/cirruslabs/tart\"\n }\n }\n}\n\nsource \"tart-cli\" \"tart\" {\n vm_base_name = \"ghcr.io/cirruslabs/macos-tahoe-base:latest\"\n vm_name = \"my-custom-tahoe\"\n cpu_count = 4\n memory_gb = 8\n disk_size_gb = 70\n ssh_password = \"admin\"\n ssh_timeout = \"120s\"\n ssh_username = \"admin\"\n}\n\nbuild {\n sources = [\"source.tart-cli.tart\"]\n\n provisioner \"shell\" {\n inline = [\"echo 'Disabling spotlight indexing...'\", \"sudo mdutil -a -i off\"]\n }\n\n # more provisioners\n}\n</code></pre> <p>Here is a repository with Packer templates used to build all the images managed by us.</p>"},{"location":"orchard/architecture-and-security/","title":"Architecture and Security","text":""},{"location":"orchard/architecture-and-security/#architecture","title":"Architecture","text":"<p>Orchard cluster consists of three components:</p> <ul> <li>Controller \u2014 responsible for managing the cluster and scheduling of resources </li> <li>Worker \u2014 responsible for executing the VMs</li> <li>Client \u2014 responsible for creating, modifying and removing the resources on the Controller, can either be an Orchard CLI or an API consumer</li> </ul> <p>At the moment, only one Controller instance is currently supported, while you can deploy one or more Workers and run any number of Clients.</p> <p>In terms of networking requirements, only Controller needs to be directly accessible from Workers and Clients, while Workers and Clients can be deployed and run anywhere (e.g. behind a NAT).</p>"},{"location":"orchard/architecture-and-security/#security","title":"Security","text":"<p>When an Orchard Client or a Worker connects to the Controller, they need to establish trust and verify that they're talking to the right Controller, so that no man-in-the-middle attack is possible.</p> <p>Similarly to web-browsers (that rely on the public key infrastructure) and SSH (which relies on semi-automated fingerprint verification), Orchard combines these two traits in a hybrid approach by defaulting to automatic PKI verification (can be disabled by <code>--no-pki</code>) and falling-back to a manual verification for self-signed certificates.</p> <p>This hybrid approach is needed because the Controller can be configured in two ways:</p> <ul> <li>Controller with a publicly valid certificate<ul> <li>can be configured manually by passing <code>--controller-cert</code> and <code>--controller-key</code> command-line arguments to <code>orchard controller run</code></li> </ul> </li> <li>Controller with a self-signed certificate<ul> <li>configured automatically on first Controller start-up when no <code>--controller-cert</code> and <code>--controller-key</code> command-line arguments are passed</li> </ul> </li> </ul> <p>Below we'll explain how Orchard client and Worker secure the connection when accessing these two Controller types.</p>"},{"location":"orchard/architecture-and-security/#client","title":"Client","text":"<p>Client is associated with the Controller using a <code>orchard context create</code> command, which works as follows:</p> <ul> <li>Client attempts to connect to the Controller and validate its certificate using host's root CA set (can be disabled with <code>--no-pki</code>)</li> <li>if the Client encounters a Controller with a publicly valid certificate, that would be the last step and the association would succeed</li> <li>if the Client is dealing with Controller with a self-signed certificate, the Client will do another connection attempt to probe the Controller's certificate</li> <li>the probed Controller's certificate fingerprint is then presented to the user, and if the user agrees to trust it, the Client then considers that certificate to be trusted for a given context</li> <li>Client finally connects to the Controller again with a trusted CA set containing only that certificate, executes the final API sanity checks, and if everything is OK then the association succeeds</li> </ul> <p>Afterward, each interaction with the Controller (e.g. <code>orchard create vm</code> command) will stick to the chosen verification method and will re-verify the presented Controller's certificate against:</p> <ul> <li>Controller with a self-signed certificate: a trusted certificate stored in the Orchard's configuration file</li> <li>Controller with a publicly valid certificate: host's root CA set</li> </ul>"},{"location":"orchard/architecture-and-security/#worker","title":"Worker","text":"<p>To make the Worker connect to the Controller, a Bootstrap Token needs to be obtained using the <code>orchard get bootstrap-token</code> command.</p> <p>While this approach provides a less ad-hoc experience than that you'd have with <code>orchard context create</code>, it allows one to mass-deploy workers non-interactively, using tools such as Ansible.</p> <p>This resulting Bootstrap Token will either include the Controller's certificate (when the current context is with a Controller with a self-signed certificate) or omit it (when the current context is with a Controller with a publicly valid certificate).</p> <p>The way Worker connects to the Controller using the <code>orchard worker run</code> command is as follows:</p> <ul> <li>when the Bootstrap Token contains the Controller's certificate:<ul> <li>the Orchard Worker will try to connect to the Controller with a trusted CA set containing only that certificate</li> </ul> </li> <li>when the Bootstrap Token has no Controller's certificate:<ul> <li>the Orchard Worker will try the PKI approach (can be disabled with <code>--no-pki</code> to effectively prevent the Worker from connecting) and fail if certificate verification using PKI is not possible</li> </ul> </li> </ul>"},{"location":"orchard/architecture-and-security/#-no-pki-override","title":"<code>--no-pki</code> override","text":"<p>If you only intend to access the Controller with a self-signed certificate and want to additionally guard yourself against CA compromises and other PKI-specific attacks, pass a <code>--no-pki</code> command-line argument to the following commands:</p> <ul> <li><code>orchard context create --no-pki</code><ul> <li>this will prevent the Client from using PKI and will let you interactively verify the Controller's certificate fingerprint before connecting, thus creating a non-PKI association</li> </ul> </li> <li><code>orchard worker run --no-pki</code><ul> <li>this will prevent the Worker from trying to use PKI when connecting to the Controller using a Bootstrap Token that has no certificate included in it, thus failing fast and letting you know that you need to create a proper Bootstrap Token</li> </ul> </li> </ul> <p>We've deliberately chosen not to use environment variables (e.g. <code>ORCHARD_NO_PKI</code>) because they fail silently (e.g. due to a typo), compared to command-line arguments, which will result in an error that is much easier to detect.</p>"},{"location":"orchard/deploying-controller/","title":"Deploying Controller","text":""},{"location":"orchard/deploying-controller/#introduction","title":"Introduction","text":"<p>Compared to Worker, which can only be deployed on a macOS machine, Controller can be also deployed on Linux.</p> <p>In fact, we've made a container image to ease deploying the Controller in container-native environments such as Kubernetes.</p> <p>Another thing to keep in mind that Orchard API is secured by default: all requests must be authenticated with the credentials of a service account. When you first run Orchard Controller, a <code>bootstrap-admin</code> service account will be created automatically and credentials will be printed to the standard output.</p> <p>If you already have a token in mind that you want to use for the <code>bootstrap-admin</code> service account, or you've got locked out and want this service account with a well-known password back, you can set the <code>ORCHARD_BOOTSTRAP_ADMIN_TOKEN</code> when running the controller.</p> <p>For example to use a secure, random value:</p> <pre><code>ORCHARD_BOOTSTRAP_ADMIN_TOKEN=$(openssl rand -hex 32) orchard controller run\n</code></pre>"},{"location":"orchard/deploying-controller/#customization","title":"Customization","text":"<p>Note that all the Deployment Methods essentially boil down to starting an <code>orchard controller run</code> command and keeping it alive.</p> <p>This means that by introducing additional command-line arguments, you can customize the Orchard Controller's behavior. Below, we list some of the common scenarios.</p>"},{"location":"orchard/deploying-controller/#customizing-listening-port","title":"Customizing listening port","text":"<ul> <li><code>--listen</code> \u2014 address to listen on (default <code>:6120</code>)</li> </ul>"},{"location":"orchard/deploying-controller/#customizing-tls","title":"Customizing TLS","text":"<ul> <li><code>--controller-cert</code> \u2014 use the controller certificate from the specified path instead of the auto-generated one (requires --controller-key)</li> <li><code>--controller-key</code> \u2014 use the controller certificate key from the specified path instead of the auto-generated one (requires --controller-cert)</li> <li><code>--insecure-no-tls</code> \u2014 disable TLS, making all connections to the controller unencrypted<ul> <li>useful when deploying Orchard Controller behind a load balancer/ingress controller</li> </ul> </li> </ul>"},{"location":"orchard/deploying-controller/#built-in-ssh-server","title":"Built-in SSH server","text":"<p>Orchard Controller can act as a simple SSH server that port-forwards connections to the VMs running in the Orchard Cluster.</p> <p>This way you can completely skip the Orchard API when connecting to a given VM and only use the SSH client:</p> <pre><code>ssh -J <service account name>@orchard-controller.example.com <VM name>\n</code></pre> <p>To enable this functionality, pass <code>--listen-ssh</code> command-line argument to the <code>orchard controller run</code> command, for example:</p> <pre><code>orchard controller run --listen-ssh 6122\n</code></pre> <p>Here's other command-line arguments associated with this functionality:</p> <ul> <li><code>--ssh-host-key</code> \u2014 use the SSH private host key from the specified path instead of the auto-generated one</li> <li><code>--insecure-ssh-no-client-auth</code> \u2014 allow SSH clients to connect to the controller's SSH server without authentication, thus only authenticating on the target worker/VM's SSH server<ul> <li>useful when you already have strong credentials on your VMs, and you want to share these VMs to others without additionally giving out Orchard Cluster credentials</li> </ul> </li> </ul> <p>Check out our Jumping through the hoops: SSH jump host functionality in Orchard blog post for more information.</p>"},{"location":"orchard/deploying-controller/#deployment-methods","title":"Deployment Methods","text":"<p>While you can always start <code>orchard controller run</code> manually with the required arguments, this method is not recommended due to lack of persistence.</p> <p>In the following sections you'll find several examples of how to run Orchard Controller in various environments in a more persistent way. Feel free to submit PRs with more examples.</p>"},{"location":"orchard/deploying-controller/#google-compute-engine","title":"Google Compute Engine","text":"<p>An example below will deploy a single instance of Orchard Controller in Google Cloud Compute Engine in <code>us-central1</code> region.</p> <p>First, let's create a static IP address for our instance:</p> <pre><code>gcloud compute addresses create orchard-ip --region=us-central1\nexport ORCHARD_IP=$(gcloud compute addresses describe orchard-ip --format='value(address)' --region=us-central1)\n</code></pre> <p>Then, ensure that there exist a firewall rule targeting <code>https-server</code> tag and allowing access to TCP port 443. If that's not the case, create one:</p> <pre><code>gcloud compute firewall-rules create default-allow-https --direction=INGRESS --priority=1000 --network=default --action=ALLOW --rules=tcp:443 --source-ranges=0.0.0.0/0 --target-tags=https-server\n</code></pre> <p>Once we have the IP address and the firewall rule set up, we can create a new instance with Orchard Controller running inside a container:</p> <pre><code>gcloud compute instances create-with-container orchard-controller \\\n --machine-type=e2-micro \\\n --zone=us-central1-a \\\n --image-family cos-stable \\\n --image-project cos-cloud \\\n --tags=https-server \\\n --address=$ORCHARD_IP \\\n --container-image=ghcr.io/cirruslabs/orchard:latest \\\n --container-env=PORT=443 \\\n --container-env=ORCHARD_BOOTSTRAP_ADMIN_TOKEN=$ORCHARD_BOOTSTRAP_ADMIN_TOKEN \\\n --container-mount-host-path=host-path=/home/orchard-data,mode=rw,mount-path=/data\n</code></pre> <p>Now you can create a new context for your local client:</p> <pre><code>orchard context create --name production \\\n --service-account-name bootstrap-admin \\\n --service-account-token $ORCHARD_BOOTSTRAP_ADMIN_TOKEN \\\n https://$ORCHARD_IP:443\n</code></pre> <p>And select it as the default context:</p> <pre><code>orchard context default production\n</code></pre>"},{"location":"orchard/deploying-controller/#kubernetes-gke-eks-etc","title":"Kubernetes (GKE, EKS, etc.)","text":"<p>The easiest way to run Orchard Controller on Kubernetes is to expose it through the <code>LoadBalancer</code> service.</p> <p>This way no fiddling with the TLS certificates and HTTP proxying is needed, and most cloud providers will allocate a ready-to-use IP-address that can directly used in <code>orchard context create</code> and <code>orchard worker run</code> commands, or additionally assigned to a DNS domain name for a more memorable hostname.</p> <p>Do deploy on Kubernetes, only three resources are needed:</p> <pre><code>apiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n name: orchard-controller\nspec:\n accessModes:\n - ReadWriteOnce\n resources:\n requests:\n storage: 1Gi\n # Uncomment this when deploying on Amazon's EKS and\n # change to the desired storage class name if needed\n # storageClassName: gp2\n---\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n name: orchard-controller\nspec:\n serviceName: orchard-controller\n replicas: 1\n selector:\n matchLabels:\n app: orchard-controller\n template:\n metadata:\n labels:\n app: orchard-controller\n spec:\n containers:\n - name: orchard-controller\n image: ghcr.io/cirruslabs/orchard:latest\n volumeMounts:\n - mountPath: /data\n name: orchard-controller\n volumes:\n - name: orchard-controller\n persistentVolumeClaim:\n claimName: orchard-controller\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: orchard-controller\nspec:\n selector:\n app: orchard-controller\n ports:\n - protocol: TCP\n port: 6120\n targetPort: 6120\n type: LoadBalancer\n</code></pre> <p>Once deployed, the bootstrap credentials will be printed to the standard output. You can inspect them by running <code>kubectl logs deployment/orchard-controller</code>.</p> <p>The resources above ensure that Controller's database is stored in a persistent storage and survives restats.</p> <p>You can further allocate a static IP address and use it by adding annotations to the <code>Service</code> resource. Here's how to do that:</p> <ul> <li>on Google's GKE: https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer-parameters#spd-static-ip</li> <li>on Amazon's EKS: https://kubernetes.io/docs/reference/labels-annotations-taints/#service-beta-kubernetes-io-aws-load-balancer-eip-allocations</li> </ul>"},{"location":"orchard/deploying-controller/#systemd-service-on-debian-based-distributions","title":"systemd service on Debian-based distributions","text":"<p>This should work for most Debian-based distributions like Debian, Ubuntu, etc.</p> <p>Firstly, make sure that the APT transport for downloading packages via HTTPS and common X.509 certificates are installed:</p> <pre><code>sudo apt-get update && sudo apt-get -y install apt-transport-https ca-certificates\n</code></pre> <p>Then, add the Cirrus Labs repository:</p> <pre><code>echo \"deb [trusted=yes] https://apt.fury.io/cirruslabs/ /\" | sudo tee /etc/apt/sources.list.d/cirruslabs.list\n</code></pre> <p>Update the package index files and install the Orchard Controller:</p> <pre><code>sudo apt-get update && sudo apt-get -y install orchard-controller\n</code></pre> <p>Finally, enable and start the Orchard Controller systemd service:</p> <pre><code>sudo systemctl enable orchard-controller\nsudo systemctl start orchard-controller\n</code></pre> <p>The bootstrap credentials will be printed to the standard output. You can inspect them by running <code>sudo systemctl status orhcard-controller</code> or <code>journalctl -u orchard-controller</code>.</p>"},{"location":"orchard/deploying-controller/#systemd-service-on-rpm-based-distributions","title":"systemd service on RPM-based distributions","text":"<p>This should work for most RPM-based distributions like Fedora, CentOS, etc.</p> <p>First, create a <code>/etc/yum.repos.d/cirruslabs.repo</code> file with the following contents:</p> <pre><code>[cirruslabs]\nname=Cirrus Labs Repo\nbaseurl=https://yum.fury.io/cirruslabs/\nenabled=1\ngpgcheck=0\n</code></pre> <p>Then, install the Orchard Controller:</p> <pre><code>sudo yum -y install orchard-controller\n</code></pre> <p>Finally, enable and start the Orchard Controller systemd service:</p> <pre><code>systemctl enable orchard-controller\nsystemctl start orchard-controller\n</code></pre> <p>The bootstrap credentials will be printed to the standard output. You can inspect them by running <code>sudo systemctl status orhcard-controller</code> or <code>journalctl -u orchard-controller</code>.</p>"},{"location":"orchard/deploying-workers/","title":"Deploying Workers","text":""},{"location":"orchard/deploying-workers/#obtain-a-boostrap-token","title":"Obtain a Boostrap Token","text":"<p>First, create a service account with a minimal set of roles (<code>compute:read</code> and <code>compute:write</code>) required for proper Worker functioning:</p> <pre><code>orchard create service-account worker-pool-m1 --roles \"compute:read\" --roles \"compute:write\"\n</code></pre> <p>Then, generate a Bootstrap Token for this service account:</p> <pre><code>orchard get bootstrap-token worker-pool-m1\n</code></pre> <p>We will reference the value of the Bootstrap Token generated here as <code>${BOOTSTRAP_TOKEN}</code> below.</p> <p>Further, we assume that Orchard controller is available on <code>orchard.example.com</code></p>"},{"location":"orchard/deploying-workers/#deployment-methods","title":"Deployment Methods","text":"<p>While you can always run <code>orchard worker run</code> manually with the required arguments, this method of deploying the Worker is not recommended.</p> <p>Instead, we've listed a more persistent methods of a Worker deployment below.</p>"},{"location":"orchard/deploying-workers/#launchd","title":"launchd","text":"<p>launchd is an init system for macOS that manages daemons, agents and other background processes.</p> <p>In this deployment method, we'll create a new job definition file for the launchd to manage on its behalf.</p> <p>To begin, first install Orchard:</p> <pre><code>brew install cirruslabs/cli/orchard\n</code></pre> <p>Ensure that the following command:</p> <pre><code>which orchard\n</code></pre> <p>...yields <code>/opt/homebrew/bin/orchard</code>. If not, you'll need to replace all of the occurences of <code>/opt/homebrew/bin/orchard</code> in the job definition below.</p> <p>Then, create a launchd job definition in <code>/Library/LaunchDaemons/org.cirruslabs.orchard.worker.plist</code> with the following contents:</p> <pre><code><?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n <dict>\n <key>Label</key>\n <string>org.cirruslabs.orchard.worker</string>\n <key>Program</key>\n <string>/opt/homebrew/bin/orchard</string>\n <key>ProgramArguments</key>\n <array>\n <string>/opt/homebrew/bin/orchard</string>\n <string>worker</string>\n <string>run</string>\n <string>--user</string>\n <string>admin</string>\n <string>--bootstrap-token</string>\n <string>${BOOTSTRAP_TOKEN}</string>\n <string>orchard.example.com</string>\n </array>\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>/bin:/usr/bin:/usr/local/bin:/opt/homebrew/bin</string>\n </dict>\n <key>WorkingDirectory</key>\n <string>/var/empty</string>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>StandardOutPath</key>\n <string>/Users/admin/orchard-launchd.log</string>\n <key>StandardErrorPath</key>\n <string>/Users/admin/orchard-launchd.log</string>\n </dict>\n</plist>\n</code></pre> <p>This assumes that your macOS user on the host is named <code>admin</code>. If not, change all occurrences of <code>admin</code> in the job definition above to <code>$USER</code>.</p> <p>Finally, change the <code>orchard.example.com</code> to the FQDN or an IP-address of your Orchard Controller.</p> <p>Now, you can start the job:</p> <pre><code>launchctl load -w /Library/LaunchDaemons/org.cirruslabs.orchard.worker.plist\n</code></pre>"},{"location":"orchard/deploying-workers/#ansible","title":"Ansible","text":"<p>If you have a set of machines that you want to use as Orchard Workers, you can use Ansible to configure them.</p> <p>We've created the cirruslabs/ansible-orchard repository with a basic Ansible playbook for convenient setup.</p> <p>To use it, clone it locally:</p> <pre><code>git clone https://github.com/cirruslabs/ansible-orchard.git\ncd ansible-orchard/\n</code></pre> <p>Make sure that the Ansible Galaxy dependencies are installed:</p> <pre><code>ansible-galaxy install -r requirements.yml\n</code></pre> <p>Then, edit the <code>production-pool</code> file and populate the following fields:</p> <ul> <li><code>hosts</code> \u2014 replace <code>worker-1.hosts.internal</code> with your worker FQDN or IP-address and add more hosts if needed</li> <li><code>ansible_user</code> \u2014 set it macOS user on the host for the SSH to work</li> <li><code>orchard_worker_user</code> \u2014 set it macOS user on the host under which the Worker will run, e.g. <code>admin</code></li> <li><code>orchard_worker_controller_url</code> \u2014 set it to FQDN or an IP-address of your Orchard Controller, for example, <code>orchard.example.com</code></li> <li><code>orchard_worker_bootstrap_token</code> \u2014 set it to <code>${BOOTSTRAP_TOKEN}</code> we've generated above</li> </ul> <p>Deploy the playbook:</p> <pre><code>ansible-playbook --inventory-file production-pool --ask-pass playbook-workers.yml\n</code></pre>"},{"location":"orchard/integration-guide/","title":"Integrating with the API","text":"<p>Orchard has a REST API that follows OpenAPI specification and is described in <code>api/openapi.yaml</code>.</p> <p>You can run <code>orchard dev</code> locally and navigate to <code>http://127.0.0.1:6120/v1/</code> for interactive documentation.</p> <p></p>"},{"location":"orchard/integration-guide/#using-the-api","title":"Using the API","text":"<p>Below you'll find examples of using Orchard API via vanilla Python's request library and Golang package that Orchard CLI build on top of.</p>"},{"location":"orchard/integration-guide/#authentication","title":"Authentication","text":"<p>When running in non-development mode, Orchard API expects a basic access authentication to be provided for each API call.</p> <p>Below you'll find two snippets that retrieve controller's information and output its version:</p>"},{"location":"orchard/integration-guide/#authentication-in-python","title":"Authentication in Python","text":"<pre><code>import requests\nfrom requests.auth import HTTPBasicAuth\n\n\ndef main():\n # Authentication\n basic_auth = HTTPBasicAuth(\"service account name\", \"service account token\")\n\n response = requests.get(\"http://127.0.0.1:6120/v1/info\", auth=basic_auth)\n\n print(response.json()[\"version\"])\n\n\nif __name__ == '__main__':\n main()\n</code></pre>"},{"location":"orchard/integration-guide/#authentication-in-golang","title":"Authentication in Golang","text":"<pre><code>package main\n\nimport (\n \"context\"\n \"fmt\"\n \"github.com/cirruslabs/orchard/pkg/client\"\n \"log\"\n)\n\nfunc main() {\n client, err := client.New()\n if err != nil {\n log.Fatalf(\"failed to initialize Orchard API client: %v\", err)\n }\n\n controllerInfo, err := client.Controller().Info(context.Background())\n if err != nil {\n log.Fatalf(\"failed to retrieve controller's information: %v\", err)\n }\n\n fmt.Println(controllerInfo.Version)\n}\n</code></pre> <p>Note that we don't provide any credentials for Golang's version of the snippet: this is because Orchard's Golang API client (<code>github.com/cirruslabs/orchard/pkg/client</code>) has the ability to read the current's user Orchard context automatically.</p>"},{"location":"orchard/integration-guide/#creating-a-vm","title":"Creating a VM","text":"<p>A more intricate example would be spinning off a VM with a startup script that outputs date, reading its logs and removing it from the controller:</p>"},{"location":"orchard/integration-guide/#creating-a-vm-in-python","title":"Creating a VM in Python","text":"<pre><code>import time\nimport uuid\n\nimport requests\nfrom requests.auth import HTTPBasicAuth\n\n\ndef main():\n vm_name = str(uuid.uuid4())\n\n basic_auth = HTTPBasicAuth(\"service account name\", \"service account token\")\n\n # Create VM\n response = requests.post(\"http://127.0.0.1:6120/v1/vms\", auth=basic_auth, json={\n \"name\": vm_name,\n \"image\": \"ghcr.io/cirruslabs/macos-tahoe-base:latest\",\n \"cpu\": 4,\n \"memory\": 4096,\n \"startup_script\": {\n \"script_content\": \"date\",\n }\n })\n response.raise_for_status()\n\n # Retrieve VM's logs\n while True:\n response = requests.get(f\"http://127.0.0.1:6120/v1/vms/{vm_name}/events\", auth=basic_auth)\n response.raise_for_status()\n\n result = response.json()\n\n if isinstance(result, list) and len(result) != 0:\n print(result[0][\"payload\"])\n break\n\n time.sleep(1)\n\n # Delete VM\n response = requests.delete(f\"http://127.0.0.1:6120/v1/vms/{vm_name}\", auth=basic_auth)\n response.raise_for_status()\n\n\nif __name__ == '__main__':\n main()\n</code></pre>"},{"location":"orchard/integration-guide/#creating-a-vm-in-golang","title":"Creating a VM in Golang","text":"<pre><code>package main\n\nimport (\n \"context\"\n \"fmt\"\n \"github.com/cirruslabs/orchard/pkg/client\"\n v1 \"github.com/cirruslabs/orchard/pkg/resource/v1\"\n \"github.com/google/uuid\"\n \"log\"\n \"time\"\n)\n\nfunc main() {\n vmName := uuid.New().String()\n\n client, err := client.New()\n if err != nil {\n log.Fatalf(\"failed to initialize Orchard API client: %v\", err)\n }\n\n // Create VM\n err = client.VMs().Create(context.Background(), &v1.VM{\n Meta: v1.Meta{\n Name: vmName,\n },\n Image: \"ghcr.io/cirruslabs/macos-tahoe-base:latest\",\n CPU: 4,\n Memory: 4096,\n StartupScript: &v1.VMScript{\n ScriptContent: \"date\",\n },\n })\n if err != nil {\n log.Fatalf(\"failed to create VM: %v\")\n }\n\n // Retrieve VM's logs\n for {\n vmLogs, err := client.VMs().Logs(context.Background(), vmName)\n if err != nil {\n log.Fatalf(\"failed to retrieve VM logs\")\n }\n\n if len(vmLogs) != 0 {\n fmt.Println(vmLogs[0])\n break\n }\n\n time.Sleep(time.Second)\n }\n\n // Delete VM\n if err := client.VMs().Delete(context.Background(), vmName); err != nil {\n log.Fatalf(\"failed to delete VM: %v\", err)\n }\n}\n</code></pre>"},{"location":"orchard/integration-guide/#resource-management","title":"Resource management","text":"<p>Some resources, such as <code>Worker</code> and <code>VM</code>, have a <code>resource</code> field which is a dictionary that maps between resource names and their amounts (amount requested or amount provided, depending on the resource) and is useful for scheduling.</p> <p>Well-known resources:</p> <ul> <li><code>org.cirruslabs.tart-vms</code> \u2014 number of Tart VM slots available on the machine or requested by the VM<ul> <li>this number is <code>2</code> for workers and <code>1</code> for VMs by default</li> </ul> </li> </ul>"},{"location":"orchard/managing-cluster/","title":"Managing the Cluster","text":""},{"location":"orchard/managing-cluster/#backups","title":"Backups","text":"<p>In order to backup the Orchard Controller, simply copy its <code>ORCHARD_HOME</code> (which defaults to <code>~/.orchard/</code>) directory somewhere safe and restore it when needed.</p> <p>This directory contains a BadgerDB database that Controller uses to store state and an X.509 certificate with key.</p>"},{"location":"orchard/managing-cluster/#upgrades","title":"Upgrades","text":"<p>Since the Orchard's initial release, we've managed to maintain the backwards compatibility between versions up to this day, so generally, it doesn't matter whether you upgrade the Controller or Worker(s) first.</p> <p>In case a new functionality is introduced, you might be required to finish the upgrade of both the Controller and the Worker(s) to be able to use it fully.</p> <p>In case there will be backwards-incompatible changes introduced in the future, we will try to do our best and highlight this in the release notes accordingly.</p>"},{"location":"orchard/managing-cluster/#observability","title":"Observability","text":"<p>Both the Controller and Worker produce some useful OpenTelemetry metrics. Metrics are scoped with <code>org.cirruslabs.orchard</code> prefix and include information about resource utilization, statuses or Workers, scheduling/pull time and many more.</p> <p>By default, the telemetry is sent to <code>https://localhost:4317</code> using the gRPC protocol and to <code>http://localhost:4318</code> using the HTTP protocol.</p> <p>You can override this by setting the standard OpenTelemetry environment variable <code>OTEL_EXPORTER_OTLP_ENDPOINT</code>.</p> <p>Please refer to OTEL Collector documentation for instruction on how to setup a sidecar for the metrics collections or find out if your SaaS monitoring has an available OTEL endpoint (see Honeycomb as an example).</p>"},{"location":"orchard/managing-cluster/#sending-metrics-to-google-cloud-platform","title":"Sending metrics to Google Cloud Platform","text":"<p>There are two standard options of ingesting metrics procuded by Orchard Controller and Workers into the GCP:</p> <ul> <li>OpenTelemetry Collector + Google Cloud Exporter \u2014 open-source solution that can be later re-purposed to send metrics to any OTLP-compatible endpoint by swapping a single exporter</li> <li>Ops Agent \u2014 Google-backed solution with a syntax similar to OpenTelemetry Collector, but tied to GCP-only</li> </ul>"},{"location":"orchard/quick-start/","title":"Quick Start","text":"<p>Tart is great for running workloads on a single machine, but what if you have more than one computer at your disposal and a couple of VMs is not enough anymore for your needs? This is where Orchard comes in to play!</p> <p>It allows you to orchestrate multiple Tart-capable hosts from either an Orchard CLI (which we demonstrate below) or through the API.</p> <p>The easiest way to start is to run Orchard in local development mode:</p> <pre><code>brew install cirruslabs/cli/orchard\norchard dev\n</code></pre> <p>This will run an Orchard Controller and an Orchard Worker in a single process on your local machine, allowing you to test both the CLI functionality and the API from a tool like cURL or programming language of choice, without the need to authenticate requests.</p> <p>Note that in production deployments, these two components are started separately and enable security by default. Please refer to Deploying Controller and Deploying Workers for more information.</p>"},{"location":"orchard/quick-start/#creating-virtual-machines","title":"Creating Virtual Machines","text":"<p>Now, let's create a Virtual Machine:</p> <pre><code>orchard create vm --image ghcr.io/cirruslabs/macos-tahoe-base:latest tahoe-base\n</code></pre> <p>You can check a list of VM resources to see if the Virtual Machine we've created above is already running:</p> <pre><code>orchard list vms\n</code></pre>"},{"location":"orchard/quick-start/#accessing-virtual-machines","title":"Accessing Virtual Machines","text":"<p>Orchard has an ability to do port forwarding that <code>ssh</code> and <code>vnc</code> commands are built on top of. All port forwarding connections are done via the Orchard Controller instance which \"proxies\" a secure connection to the Orchard Workers.</p> <p>Therefore, your workers can be located under a stricter firewall that only allows connections to the Orchard Controller instance. Orchard Controller instance is secured by default and all API calls are authenticated and authorized.</p>"},{"location":"orchard/quick-start/#ssh","title":"SSH","text":"<p>To SSH into a VM, use the <code>orchard ssh</code> command:</p> <pre><code>orchard ssh vm tahoe-base\n</code></pre> <p>You can specify the <code>--username</code> and <code>--password</code> flags to specify the username/password pair to use for the SSH protocol. By default, <code>admin</code>/<code>admin</code> is used.</p> <p>You can also execute remote commands instead of spawning a login shell, similarly to how OpenSSH's <code>ssh</code> command accepts a command argument:</p> <pre><code>orchard ssh vm tahoe-base \"uname -a\"\n</code></pre> <p>You can execute scripts remotely this way, by telling the remote command-line interpreter to read from the standard input and using the redirection operator as follows:</p> <pre><code>orchard ssh vm tahoe-base \"bash -s\" < script.sh\n</code></pre>"},{"location":"orchard/quick-start/#vnc","title":"VNC","text":"<p>Similarly to <code>ssh</code> command, you can use <code>vnc</code> command to open Screen Sharing into a remote VM:</p> <pre><code>orchard vnc vm tahoe-base\n</code></pre> <p>You can specify the <code>--username</code> and <code>--password</code> flags to specify the username/password pair to use for the VNC protocol. By default, <code>admin</code>/<code>admin</code> is used.</p>"},{"location":"orchard/quick-start/#deleting-virtual-machines","title":"Deleting Virtual Machines","text":"<p>The following command will delete the VM we've created above and clean-up the resources associated with it:</p> <pre><code>orchard delete vm tahoe-base\n</code></pre>"},{"location":"orchard/quick-start/#environment-variables","title":"Environment variables","text":"<p>In addition to controlling the Orchard via the CLI arguments, there are environment variables that may be beneficial both when automating Orchard and in daily use:</p> Variable name Description <code>ORCHARD_HOME</code> Override Orchard's home directory. Useful when running multiple Orchard instances on the same host and when testing. <code>ORCHARD_LICENSE_TIER</code> The default license limit only allows connecting 4 Orchard Workers to the Orchard Controller. If you've purchased a Gold Tier License, set this variable to <code>gold</code> to increase the limit to 20 Orchard Workers. And if you've purchased a Platinum Tier License, set this variable to <code>platinum</code> to increase the limit to 200 Orchard Workers. <code>ORCHARD_URL</code> Override controller URL on per-command basis. <code>ORCHARD_SERVICE_ACCOUNT_NAME</code> Override service account name (used for controller API auth) on per-command basis. <code>ORCHARD_SERVICE_ACCOUNT_TOKEN</code> Override service account token (used for controller API auth) on per-command basis."},{"location":"orchard/using-orchard-cli/","title":"Using Orchard CLI","text":""},{"location":"orchard/using-orchard-cli/#installation","title":"Installation","text":"<p>The easiest way to install Orchard CLI is through the Homebrew:</p> <pre><code>brew install cirruslabs/cli/orchard\n</code></pre> <p>Binaries and packages for other architectures can be found in GitHub Releases.</p>"},{"location":"orchard/using-orchard-cli/#setting-up-a-context","title":"Setting up a context","text":"<p>The first step after installing the Orchard CLI is to configure its context. Configuring context is like pairing with the specified Orchard Controller, so that the commands like <code>orchard create vm</code>, <code>orchard ssh vm</code> will work.</p> <p>To configure a context, <code>orchard context</code> has a subfamily of commands:</p> <ul> <li><code>orchard context create <CONTROLLER ADDRESS></code> \u2014 creates a new context to communicate with Orchard Controller available on the specified address</li> <li><code>orchard context default <CONTROLLER ADDRESS></code> \u2014 sets a context with a given Orchard Controller address as default (in case there's more than one context configured)</li> <li><code>orchard context list</code> \u2014 lists all the configured contexts, indicating the default one</li> <li><code>orchard context delete <CONTROLLER ADDRESS></code> \u2014 deletes a context for the specified Orchard Controller address</li> </ul> <p>Most of the time, you'll only need the <code>orchard context create</code>. For example, if you've deployed your Orchard Controller to <code>orchard-controller.example.com</code>, a new context can be configured like so:</p> <pre><code>orchard context create orchard-controller.example.com\n</code></pre> <p><code>orchard context create</code> assumes port 6120 by default, so if you use a different port for the Orchard Controller, simply specify the port explicitly:</p> <pre><code>orchard context create orchard-controller.example.com:8080\n</code></pre> <p>When creating a new context you will be prompted for the service account name and token, which can be obtained from:</p> <ul> <li><code>orchard controller run</code> logs<ul> <li>if this is a first start</li> </ul> </li> <li><code>orchard get service-account</code><ul> <li>from an already configured Orchard CLI</li> </ul> </li> </ul>"},{"location":"orchard/using-orchard-cli/#using-labels-when-creating-vms","title":"Using labels when creating VMs","text":"<p>Labels are useful if you want to restrict scheduling of a VM to workers whose labels include a subset of the VM's specified labels.</p> <p>For example, you might have an Orchard Cluster consisting of the following workers:</p> <ul> <li>Mac Minis (<code>orchard worker run --labels location=DC1-R12-S4,model=macmini</code>)</li> <li>Mac Studios (<code>orchard worker run --labels location=DC1-R18-S8,model=macstudio</code>)</li> </ul> <p>To create and run a VM specifically on Mac Studio machines, pass the <code>--labels</code> command-line argument to <code>orchard create vm</code> when creating a VM:</p> <pre><code>orchard create vm --labels model=macstudio <NAME>\n</code></pre> <p>When processing this VM, the scheduler will only place it on available Mac Studio workers.</p>"},{"location":"orchard/using-orchard-cli/#using-resources-when-creating-vms","title":"Using resources when creating VMs","text":"<p>Resources are useful if you want to restrict scheduling of a VM to workers that still have enough of the specified resource to fit the VM's requirements.</p> <p>The difference between the labels is that the resources are finite and are automatically accounted by the scheduler.</p> <p>To illustrate this with an example, let's say you have an Orchard Cluster consisting of the following workers:</p> <ul> <li>Mac Mini with 1 Gbps bandwidth (<code>orchard worker run --resources bandwidth-mbps=1000</code>)</li> <li>Mac Studio with 10 Gbps bandwidth (<code>orchard worker run --resources bandwidth-mbps=10000</code>)</li> </ul> <p>VM created using the command below will only be scheduled on a Mac Studio with 10 Gbps bandwidth:</p> <pre><code>orchard create vm --resources bandwidth-mbps=7500 <NAME>\n</code></pre> <p>However, after this VM is scheduled, the 10 Gbps Mac Studio will only be able to accommodate one more VM (due to internal Apple EULA limit for macOS virtualization) with <code>bandwidth-mbps=2500</code> or less.</p> <p>After the VM finishes, the unused resources will be available again.</p>"},{"location":"orchard/using-orchard-cli/#automatic-resources","title":"Automatic resources","text":"<p>In addition to manually specifying resources when starting a worker, the following resources are discovered and set automatically by the worker for convenience:</p> <ul> <li><code>org.cirruslabs.logical-cores</code> \u2014 number of logical cores on the host</li> <li><code>org.cirruslabs.memory-mib</code> \u2014 total memory in MiB (mebibytes) on the host</li> </ul> <p>Note that the values for these resources are scraped only once at worker startup.</p>"},{"location":"blog/archive/2025/","title":"2025","text":""},{"location":"blog/archive/2024/","title":"2024","text":""},{"location":"blog/archive/2023/","title":"2023","text":""},{"location":"blog/category/announcement/","title":"announcement","text":""},{"location":"blog/category/orchard/","title":"orchard","text":""}]} |