The Xavier System is a remote containerized development environment, which uses Visual Studio Code, the Remote - SSH extension and an AWS EC2 instance as backend. This project is sourced from the original Xavier and the aws-admin projects, now deprecated in favor of the Xavier System.
By taking advantage of Visual Studio Code features like SSH Agent forwarding and Environment Variable Inheritance, the Xavier System allows the user to enjoy a fully dynamic remote development environment with the same benefits and ease of use as developing locally.
The Xavier System is a "micro framework", which allows the user to manage a dynamic containerized environment to accommodate multiple projects in the same instance. The xv tools can mutate the whole development environment with a single command, using Docker to build and deploy containers.
Using Xavier System, the user can keep it's PC clean from resource demanding systems like Docker Desktop, Virtual Machines or WSL systems. All that the user needs, is Visual Studio Code, the AWS CLI v2 utility with the Session Manager plugin, some custom shell functions and a Nerd Font for the Oh My ZSH framework.
The Xavier System uses the concept of "System Overlay". The "xv-container", in Xavier System's terminology, assumes the OS role. This is the core principle of the Xavier System. Only a single container can assume the OS role at time. If the user needs to change the current xv-container to another one, the xv tool can build and deploy another xv-container, replacing the old one. This way, there is always a single xv-container running in the instance. This helps the user to not waste resources and keeping the system organized.
The current xv-container running on the system always assumes the container name of "xv-container", but the image name and the container hostname receives the name of the Dockerfile project directory, for example, when using xv-utils source as the xv-container, the image name and the hostname is set to "xv-utils".
The xv tool is the "brain" of the Xavier System. It builds and deploys the xv-container using the user's custom configuration in the xv.yaml file to setup container mounts, environment variables and ports, similar to a Docker Compose file. The xv.yaml file also have the concept of a global shared configuration, with allows the user to set common mounts, environment variables or ports to all xv-containers launched by the xv tool.
By default, any xv-container is supposed to bind it's OpenSSH service on the default port 22 to the port 2222 on the instance's IPv4 localhost (127.0.0.1). Using AWS SSM tunneling and the xvpf function, the user bind's a local port into the instance's localhost on port 2222. This is a very secure practice, since both the instance or the container doesn't need to listen to SSH connections from outside AWS. No need for Public Subnets or Inbound Security Group Rules. Although, the Xavier System itself needs Internet access to download binaries and images. At least a NAT Gateway or a proxy server should be supplied for the Xavier System instance.
The default base distro image used by Xavier System sources is Fedora 36, although, the user can change the distro in the Dockerfiles to Ubuntu or Debian, by commenting/uncommenting code for the xv-utils and the cdk-base Dockerfiles.
Note that ALPINE IMAGES AREN'T SUPPORTED. Alpine is a perfect distro for slim containers, but it uses musl instead of the standard glibc like most of the Linux distros. Visual Studio Code Remote - SSH extension isn't compatible with any non-glibc distros. This information is mentioned here.
The default installation of the Xavier System bundles the Oh My ZSH framework with the Powerlevel10k ZSH theme maintained by Roman Perepelitsa. The installation can be customized by creating the configuration file xv-setup.yaml file, using the xv-setup.example.yaml file as source. For more information, read "The xv-setup tool" section.
The installation customization allows the user to choose various Oh My ZSH plugins and themes, built-in or custom. Also, allows the user to avoid Oh My ZSH entirely. It's installation uses the xv-setup container tool, which uses an Ansible playbook for both setup and updates.
To automate the environment creation, the user can customize one of the bundled AWS CloudFormation templates in the cfn/ directory, available for both AMD64 (x86_64) or ARM64 (aarch64) architectures.
The CloudFormation template uses the minimal Amazon Linux 2022 AMI with it's default kernel. The Amazon Linux 2022 is the latest version of Amazon Linux, now rebased to Fedora. AWS team is doing a terrific job on optimizing the Amazon Linux 2022 distro. For this reason, Amazon Linux 2022 is the default base distro for the Xavier System project. The base distro can be customized as well, but since it will only be used as base for the Xavier System, there is no much benefit here. Also, the xv tool uses the official Amazon Linux 2022 container image as base too.
-
Install Visual Studio Code in your PC. Installation can be either "User" or "System". Use which suits you best.
-
Install any of the Nerd Fonts available. I suggest the patched version of Inconsolata, which can be downloaded for Windows here or MacOS/Linux here.
-
Open Visual Studio Code and configure the property
"terminal.integrated.fontFamily"to"Inconsolata NF"or any other Nerd Font you downloaded. Also, I recommend to disable the experimental "Integrated Shell" feature and enable environment inheritance. Yoursettings.jsoncan look like this:{ "terminal.integrated.fontFamily": "Inconsolata NF", "terminal.integrated.shellIntegration.enabled": false, "terminal.integrated.inheritEnv": true } -
Install and enable the Remote - SSH extension on your Visual Studio Code installation. For more information, refer here.
-
Generate an OpenSSH key pair for connecting to Xavier System, preferably using the Ed25519 private key format, to generate a very secure key with a very small public key. You can use any other SSH key pair you already have. The command bellow works in any modern OS, even Windows 10+ using Powershell. Remember to add a secure passphrase to your key.
ssh-keygen -t ed25519 -i ~/.ssh/firstlast-xavier-system -C [email protected] -
Create and/or add the following configuration to the OpenSSH file
~/.ssh/config. Since server keys will change constantly, I recommend adding theStrictHostKeyChecking nooption and setting theUserKnownHostsFileto NULL:Host xavier HostName localhost Port 2222 User root IdentityFile ~/.ssh/firstlast-xavier-system ForwardAgent yes StrictHostKeyChecking no # For Windows UserKnownHostsFile \\.\NUL # For MacOS/Linux UserKnownHostsFile /dev/null -
Install and configure the AWS CLI v2 utility and the Session Manager plugin. They will allow you to deploy and to connect to your EC2 instance using the AWS SSM Session Manager. Follow the documentation here for the CLI and here for the plugin. Use
aws sts get-caller-identitycommand to test the AWS CLI v2 installation and configuration. -
Configure
xv*helper functions to interact with your Xavier System. The Xavier System relies on the usage of AWS SSM Documents to bind local and remote ports on the EC2 instance in a very secure way. You don't need to add any specific value to theInstanceIdvariable now. After the CloudFormation stack is deployed, you can retrieve the EC2 Instance ID.# Powershell (Windows, MacOS or Linux) # Edit profile with: code $PROFILE # Reload profile with: . $PROFILE $InstanceId = "i-xxx" function xvstart { aws ec2 start-instances --instance-ids $InstanceId } function xvstop { aws ec2 stop-instances --instance-ids $InstanceId } function xvpf { param ( [Parameter(Mandatory,Position=0)] [string] $LocalPortNumber, [Parameter(Mandatory,Position=1)] [string] $RemotePortNumber ) aws ssm start-session --target $InstanceId --document-name AWS-StartPortForwardingSession --parameters "portNumber=${RemotePortNumber},localPortNumber=${LocalPortNumber}" } function xvcmd { param ( [Parameter(Mandatory,Position=0)] [string] $Command ) aws ssm start-session --target $InstanceId --document-name AWS-StartInteractiveCommand --parameters "command=[${Command}]" } function xvssh { aws ssm start-session --target $InstanceId }
# BASH or ZSH (MacOS or Linux) # You can add these functions directly to .bashrc or .zshrc files. InstanceId="i-xxx" xvstart() { aws ec2 start-instances --instance-ids $InstanceId } xvstop() { aws ec2 stop-instances --instance-ids $InstanceId } xvpf() { aws ssm start-session --target $InstanceId --document-name AWS-StartPortForwardingSession --parameters "portNumber=${1},localPortNumber=${2}" } xvcmd() { aws ssm start-session --target $InstanceId --document-name AWS-StartInteractiveCommand --parameters "command=[${1}]" } xvssh() { aws ssm start-session --target $InstanceId }
-
Create a AWS CloudFormation stack by modifying one of the templates in the
cfndirectory. Modify the default values to customize the setup. Thestack-arm64.yamldeploys a Graviton2 ARM64t4g.mediumInstance by default. Thestack-amd64.yamldeploys an AMD x86t3a.mediumInstance by default. ARM instances are slightly cheaper and faster than it's x86 counterparts. I suggest you to use ARM instead of x86, unless you're using applications and packages that are unavailable for ARM.aws cloudformation deploy --stack-name my-xavier-system --template-file stack-arm64.yaml --capabilities CAPABILITY_NAMED_IAM -
After the deploy is finished, you can retrieve the Xavier System instance ID to update the
InstanceIdvariable for the functions.aws cloudformation describe-stacks --stack-name my-xavier-system --query "Stacks[0].Outputs[0].OutputValue" -
Use the
xvsshfunction to access the instance shell through thessm-user. Then access the Xavier System path/opt/xavier/systemas root:$ xvssh $ sudo su - # cd /opt/xavier/system
-
Start the Xavier System installation by running the
./xv-setup.shscript. If you want to customize the setup, copy thexv-setup.example.yamlfile into thexv-setup.yamlfile, then edit it's contents. Then run./xv-setup.sh. -
The installation can take some minutes to complete. Enjoy the moment to grab a coffee.
-
Finally, use installed
xvto build and run thexv-utilsexample container. Exitxvsshafterwards:# xv xv-utils # exit $ exit
-
Use
xvpffunction to start a tunnel using SSM from port2222on the local OS to2222on the Xavier Instance. This is a blocking function and you can terminate it withCTRL+C.xvpf 2222 2222 -
Open another terminal window. Use
codeto access thexv-utilscontainer shell:code --remote ssh-remote+xavier /xavier -
The
xv-utilscontainer have access to all Xavier System files. It can be used to create/edit Dockerfiles, thesource/xv.yamlfile or to make tweaks on the Xavier System directorysystem. For more information on how to configurexv.yaml, read Configuring xv-containers. -
If you installed Xavier System with Oh My ZSH and the Powerlevel10k theme, run the
p10k configureutility from Visual Studio Code Integrated Terminal to customize the theme. -
Go to the
/xavier/sourcesdirectory and start designing your containers. You can use the included examples in the directory/xavier/system/examples/sourcesas base. For more information on building customxv-containersusing thexv-utilstool, read Creating xv-containers using xv-utils.
The Xavier System uses a directory structure inside the /opt/xavier path. The xv-utils container, by default, mounts this path into the /xavier directory. These are the Xavier System's subdirectories:
-
root/- This is the common shared/rootdirectory for allxv-containers. This will allow everyxv-containerto share a common Oh My ZSH theme and plugins. Also share binaries and scripts installed under/root/bin/. This directory, however, is not intended for storing GIT repositories or other user's work. For that, use theworkspace/directory. -
sources/- This is where things happen: Each one of thesources/subdirectories contains Dockerfiles and other files that are used during thexv-containerbuilds by thexvtool. Also, thexv.yamlconfiguration file is located there. It's the user's main configuration file for thexvtool. The name of the subdirectory is the actual name of thexv-container. For more information, read "The xv tool" section. -
system/- The clone of Xavier System's GIT repository is here. There the user can create a customxv-setup.yamlfile to customize the Xavier System installation. Also, thexv-setup.shscript can be used to update Xavier System configuration. For more information, read "The xv-setup tool" section. -
tools/- This is a reserved directory for user-managed container tools sources. Container tools are single-run or service helper containers, like thexvor theamazon/aws-clicontainer tools. The support to build and manage container tools is planned for future releases of the Xavier System project. -
workspace/- This directory, by default, is mounted on allxv-containersin the/workspacedirectory. The goal of this directory is to store there all user's GIT repositories and/or temporary directories for testing betweenxv-containers.
The xv tool is quite simple. It's a containerized Python script that uses the python-on-whales library maintained by Gabriel de Marmiesse to handle xv-containers builds and deployment. It must run as root (using sudo) and expects the name of the xv-container as argument. The example bellow will build xv-utils image and start it as the xv-container. If there is already a xv-container running, the xv tool will REPLACE it:
xvcmd "sudo xv xv-utils"WARNING: The xv tool is under constant development and isn't handling exceptions correctly.
The xv tool main configuration file is the xv.yaml file, located under source/ in the Xavier System's directory structure. There is a brief explanation on how it works:
# Under "shared" the user can configure mounts, environments and ports that will be available to all xv-containers.
shared:
# For "ports" it's mandatory to set all the 3 values, host, container and protocol.
# If there is no ports to bound, set "ports:" to "ports: []"
ports:
- host: 127.0.0.1:2222 # This is the port that will be bound in the instance.
container: 22 # This is the port that will be bound to the xv-container.
protocol: tcp # This is the port protocol, tcp or udp.
# For "environments", "name" and "value" must be set for every environment.
environment:
- name: BUILT_BY
value: xavier-system
# Under "volumes", set the bind mounts that will be exposed to xv-containers.
# Please note that Docker Volumes aren't supported, just bind mounts.
# It's mandatory to set all the 3 values, source, target and mode.
volumes:
- source: /opt/xavier/root # This is the path related to the HOST OS.
target: /root # This is the path where the "source" will be bound into the xv-container.
mode: rw # This is the mount mode. Only sets to rw if the container needs to modify files there.
- source: /opt/xavier/workspace
target: /workspace
mode: rw
# Notice that, by default, the Docker binary and UNIX socket is mounted on all containers. This will allow xv-containers to run container tools.
- source: /usr/bin/docker
target: /usr/bin/docker
mode: ro
- source: /var/run/docker.sock
target: /var/run/docker.sock
mode: rw
# Under "containers", the user can set the same options as above. The only difference here is that the ports, environments and volumes listed are available only to a specific xv-container. This way, the user can customize the container with unique features.
# The keys under "containers" MUST MATCH the name of the subdirectory under "sources/". the xv tool uses this information to properly build and deploy the xv-container.
containers:
cdk-base:
# No extra ports means only the shared ports are available. The key must be set to "[]" or the xv tool will produce an error.
ports: []
environment:
# Change the AWS_PROFILE variabke
- name: AWS_PROFILE
value: myprofile
volumes:
# Mouting the AWS CLI config directory into the xv-container is only necessary if the user is developing applications that are using the AWS SDK to access AWS resources and need to test this integration, like CDK do.
# For the actual AWS CLI usage, the Xavier System's fake "aws" utility automatically mounts the .aws config directory without the need to expose it to the container.
- source: /root/.aws
target: /root/.aws
mode: ro
xv-utils:
ports: []
environment:
# This environment prevents the powerlevel10k theme to automatically launch it's configuration wizard. This can be removed if the user changed the theme on the xv-setup.yaml configuration file.
- name: POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD
value: "true"
volumes:
# This mount allows xv-utils to have full access on all Xavier System's directory structure for administration.
- source: /opt/xavier
target: /xavier
mode: rw
# This mount exposes the AWS CLI config directory on xv-utils for visualization and customization. Instead of using "aws configure", the user can setup the profiles on the "config" file by hand.
- source: /root/.aws
target: /xavier/awsconfig
mode: rw
# New container example:
# The user must create a new subdirectory under "sources/" with the same name here.
# In this case, the directory "sources/eks-hero/" needs to be created with a Dockerfile inside.
eks-hero:
ports:
# This can be useful to expose EKS port-forwarding through SSM tunneling to the local machine.
- host: 127.0.0.1:8080
container: 8080
protocol: tcp
environment:
- name: AWS_PROFILE
value: profile_with_eks_cluster_access
volumes: []
# Minimal config
simple-container:
ports: []
environment: []
volumes: []The xv-setup.sh script runs the xv-setup container tool. The xv-setup container run an Ansible playbook that setups and/or updates the Xavier System dependencies and features.
It uses the xv-setup.yaml file to select which examples sources and tools will be installed when the the playbook runs. If the xv-setup.yaml file isn't found, the xv-setup tool will generate a new one by copying the default xv-setup.example.yaml file.
The user can run this tool multiple times to add new features and update it's GIT repositories.
Please note that the xv-setup tool don't delete anything. If the user wants to remove a theme or a plugin, it must do it by hand.
Also, it's possible for the user to disable the Oh My ZSH framework or set the enabled plugins and theme for it.
For custom plugins, the user can add multiple GIT repositories and the name of the directory that will hold the plugin. The support for the theme is similar, but it only allows the user to set a single custom theme.
The default xv-setup.yaml file:
install:
# Change to False here to disable Oh My ZSH installation
ohmyzsh: True
examples:
# By default, all example sources will be installed
sources:
- xv-utils
- cdk-base
tools:
# By default, all scripts will be installed
scripts:
- eks-kubeconfig.sh
- get-kubectl.sh
- update-eksctl.sh
ohmyzsh:
plugins:
# By default, the xv-setup will clone the "zsh-autosuggestions" and "zsh-syntax-highlighting" plugins.
# Also, enable them along with the builtin "git" and "history-substring-search" plugins
enabled:
- git
- history-substring-search
- zsh-autosuggestions
- zsh-syntax-highlighting
custom:
- path: zsh-autosuggestions
repo: https://github.com/zsh-users/zsh-autosuggestions.git
- path: zsh-syntax-highlighting
repo: https://github.com/zsh-users/zsh-syntax-highlighting.git
theme:
# By default, the powerlevel10k custom theme is cloned and set as current theme
enabled: powerlevel10k/powerlevel10k
custom:
path: powerlevel10k
repo: https://github.com/romkatv/powerlevel10k.gitCreating xv-containers needs that the user have some familiarity with Dockerfiles and container building.
The first step is to give a new name and a purpose to the new xv-container. Let's call it git-manager. The purpose of this one is manage GIT repositories.
The second step is to add a new entry to the /xavier/sources/xv.yaml file for the git-manager:
...
containers:
...
git-manager:
ports: []
environment: []
volumes: []Since the git-manager xv-container don't need any extra ports, environments or volumes, we can just add empty lists to it.
The next step is to create a new directory in the /xavier/sources directory. To save us from the trouble of creating everything from the scratch, we can use the base source examples located in /xavier/system/examples/sources. There are 2 example sources included there: fedora-base for bootstrapping new Fedora sources and ubuntu-debian-base for bootstrapping new Debian or Ubuntu sources.
Let's say the git-manager will be a Debian 10 xv-container. Let's copy the /xavier/system/examples/sources/ubuntu-debian-base directory to /xavier/sources/git-manager:
cp -r /xavier/system/examples/sources/ubuntu-debian-base /xavier/sources/git-managerOn the git-manager Dockerfile we need to make a few changes. Most of it already fulfil our purposes. It already have git and openssh-client packages listed for build and the proper sshd configuration. We need tp modify the base image to FROM debian:10-slim. For that, open the Dockerfile and replace the first line:
FROM debian:10-slim
...Now the git-manager source is ready to become a xv-container. Close the remote Visual Studio Code window and run xvcmd "sudo xv git-manager" in local terminal. The xv tool will build the git-manager source and deploy it as the xv-container afterwards. Open a new remote Visual Studio Code window using:
code --remote ssh-remote+xavier /workspaceAfter connecting the remote xavier instance, open the integrated terminal. You can clone or create GIT repositories in the /workspace directory.
This second tutorial is an example of a xv-container with a very specific purpose: Manage an AWS EKS cluster. Let's call this source eks-hero. In this example, the EKS cluster is allowing only a specific IAM role to access it's API. In this case, you need a specific profile configured on the /xavier/awsconfig/config file (This is a bind mount for the /root/.aws directory in the host OS).
To properly configure an AWS CLI profile to assume a specific role, refer to this link.
Let's say we created the profile eks_dev for this purpose. Let's add the eks-hero source to the /xavier/sources/xv.yaml file:
...
containers:
...
eks-hero:
ports: []
environment:
- name: AWS_PROFILE
value: eks_dev
volumes: []For the base source, let's use the fedora-base source to build a Fedora 36 xv-container:
cp -r /xavier/system/examples/sources/fedora-base /xavier/sources/eks-heroLet's add the helm and jq packages to help us to deploy Charts and to parse JSON files easily:
FROM fedora:36
RUN dnf update -y \
&& dnf install -y \
git \
helm \ # Added
jq \ # Added
passwd \
openssh-clients \
openssh-server \
shadow-utils \
zsh \
&& dnf clean all
...Save the Dockerfile, close the remote Visual Studio Code window and run xvcmd "sudo xv eks-hero". Let's open the remote Visual Studio Code using code --remote ssh-remote+xavier /workspace.
Now you can use the helper script tools eks-kubeconfig.sh, get-kubectl.sh and update-eksctl.sh to proper configure the kubeconfig, download the desired kubectl client version and to install/update eksctl.
It's a simple but very useful script installed into Xavier System's root/bin/ directory that runs a amazon/aws-cli container, mounting the host /root/.aws/ directory and setting the AWS_PROFILE environment from xv-container own environment. All the arguments are passed directly into the docker run command. For the user perception, it's like running the actual aws command.
This helps the user to save time writing Dockerfiles, since the only method available to install the version 2 of the AWS CLI is by manually downloading the installer and running it during the container build.
For information on the xv-utils tool, read Creating xv-containers using xv-utils.
This source have all the necessary dependencies to develop and run AWS CDK projects using Python. The user don't need to use or configure Python's VirtualEnv here.
To create a new CDK project, create a GIT repository, clone it to the /workspace/mycdkproject directory, then run cdk init --language python --generate-only inside of it.
To use Pylint and Pylance recommendations, enable the official Python extension in Visual Studio code for the remote system (not local). Then add the following to the end of the Visual Studio Code settings.json if not there already:
{
...existing code...,
"python.linting.enabled": true,
"python.linting.pylintEnabled": true
}This script is very similar to the "fake" aws script installed by default, but with the purpose to generate a /root/.kube/config file to use with kubectl to access an EKS cluster. The difference is the extra mounting of the root/.kube directory and the eks update-kubeconfig --name arguments. The user only needs to supply the name of the EKS cluster.
Example:
eks-kubeconfig.sh my-eks-clusterThis is an utility script that deals with the kubectl versioning issues. kubectl can only supports Kubernetes clusters with the same version, one earlier or one later. For example: kubectl for Kubernetes 1.21 can only support Kubernetes clusters from 1.20-1.22.
By passing a Kubernetes version to this script as argument, it will download the official kubectl binary from AWS for the correct system architecture and install it on /root/bin/ directory, replacing any version already there. Example:
get-kubectl.sh 1.22This is a simple script that downloads the most recent version of the eksctl tool into the /root/bin/ directory. Like the get-kubectl.sh script, it detects the system architecture and download the proper binary. No arguments needed.
By default, the OpenSSH Authentication Agent service is disabled in Windows. It can enabled through the services.msc console or using Powershell as admin:
Get-Service ssh-agent | Set-Service -StartupType Automatic -PassThru | Start-ServicePlease, if you want to contribute with the project, open an issue or submit a PR. Contributors are always welcome! :)
Copyright 2022 Eduardo Medeiros Silva.
Licensed under GNU General Public License v3.0.
