There are multiple ways to solve the dev environment question whereas having a local development environment may be superior to any other setup but a cloud-based environment that is close to your workloads have it’s set of benefits as well.
Local vs Cloud environment
Performance and smoothness of your local environment is something no other setup can bit (assuming you have good-enough hardware) but even with top-notch hardware you will face a lot of detious things to set up, configure and follow to make your environment ready. To name a few — configure and whitelist your IP to talk to the databases or maybe configure VPN. Install a particular version of Python or Node JS your team is working with and don’t forget about all the extensions in your favourite IDE to make it a smooth ride. And don’t forget that you may need some specific piece of software that only runs under a particular operation system or maybe can’t run outside of a secure perimeter.
On the other hand cloud-based and repeatable setup may give you a piece of mind with the majority of these. You can have the VMs whitelisted for proper access from the get go without having to ask anyone for extra assistance. You may install all your favourite or just required toolsets and you can also pre-package extensions of your choice and built them into the setup.
Google Cloud Workstations customizable setup
Google Cloud Workstations provide a way to built exactly that setup for your engineers that gives you ability to create different configurations with the desirable runtime container which all the things you need. Workstations are managed by Cloud Workstations control plane, but the virtual machines themselves reside in your VPC so you can control what and how they can access in your setup. You can e.g. disable public IP for a VM and configure Cloud NAT to ensure smooth access to restricted resources.
Minimal Google Cloud Workstations setup with Pulumi
In order to give it a taste and spin it up ASAP we’ll go with a minimal setup while still utilizing infrastructure best practices by using Pulumi for our infrastructure as a code and Typescript to have a coherent dev-friendly setup. We will also use Bun for it’s ease and speed (you can use any other environment of choice).
Check out the complete setup here.
Bootstrapping a project
In order to bootstrap a new Pulumi project we’ll need Pulumi CLI and Bun CLI and gcloud CLI:
curl -fsSL https://get.pulumi.com | bash
curl -fsSL https://bun.com/install | bash
curl -fsSL https://sdk.cloud.google.com | bash
You will also need to create Pulumi account and login with:
pulumi login
If you haven’t done this before, you’ll need to login with application-default credentials in gcloud CLI:
gcloud auth application-default login
And now either create a new GCP project or set the one you’re willing to use:
gcloud projects create --name="My project Name"
# OR
gcloud config set project
Now here’s how you can bootstrap a project using Pulumi CLI:
mkdir gcp-cloud-workstations && cd gcp-cloud-workstations
pulumi new typescript \
--language=typescript \
--runtime-options=packagemanager=bun \
--name=gcp-cloud-workstations \
--description='Minimal setup of the GCP Cloud Workstations with Typescript and Bun' \
--stack=main
You can also just use plain pulumi new typescript and follow the project creation wizard. So in the abovementioned command we’ve asked Pulumi to create a Typescript-based project with Bun as a package manager with a defined name, description and stack name.
It will take care of creating index.ts , tsconfig and package.json as well as Pulumi.yaml .
Let’s now add @pulumi/gcp provider:
bun add -E '@pulumi/gcp'
And set required project ID config for the provider:
pulumi config set 'gcp:project'
We’re all set to start writing up our infrastructure as code now.
Adding IaaC to the project
Let’s start by defining a dedicated GCP provider instance in our code.
import * as gcp from "@pulumi/gcp";const region = "us-central1";
const gcpProvider = new gcp.Provider("gcpProvider", {
project: gcp.config.project,
region: region,
defaultLabels: { team: "devops" },
});
It’s a recommended best practice that will ensure that all our resources are created with a particular defaults and also has a handy feature such as adding labels to all eligible resources.
Enabling GCP services
If you’re going to add Cloud Workstations into a new GCP project, one will need required GCP services to be enabled, also we can do that from code as well. new gcp.project.Service creates a service resource that manages enablement of services in GCP.
const enableServices = (services: string[]) => {
return services.map((service) => {
return new gcp.projects.Service(
service,
{
service: service,
project: gcp.config.project,
},
{ provider: gcpProvider },
);
});
};const requiredServices = [
"compute.googleapis.com",
"workstations.googleapis.com",
];
const services = enableServices(requiredServices);
You can also enable services using gcloud CLI if you don’t want to have this managed with IaaC:
gcloud services enable compute.googleapis.com workstations.googleapis.com
Setting up Workstations cluster and configuration
Now when the services are enabled, we can configure the workstations and related resources.
export default async function main() {
const wsNetwork = new gcp.compute.Network(
"wsNetwork",
{
autoCreateSubnetworks: false,
},
{
provider: gcpProvider,
dependsOn: services,
},
);
const wsSubnetwork = new gcp.compute.Subnetwork(
"wsUsCentral1Subnet",
{
network: wsNetwork.id,
region: region,
ipCidrRange: "10.128.0.0/20",
},
{
provider: gcpProvider,
parent: wsNetwork,
},
);
const wsCluster: gcp.workstations.WorkstationCluster =
new gcp.workstations.WorkstationCluster(
"developmentCluster",
{
workstationClusterId: "test-cluster",
network: wsNetwork.id,
subnetwork: wsSubnetwork.id,
location: region,
displayName: "Test Cluster",
annotations: {
description: "Minimal cluster for testing",
},
labels: {
purpose: "test",
},
},
{ provider: gcpProvider, dependsOn: services },
);
const wsMinimalConfig = new gcp.workstations.WorkstationConfig(
"wsMinimalConfig",
{
workstationConfigId: "minimal-config",
workstationClusterId: wsCluster.workstationClusterId,
location: region,
},
{ provider: gcpProvider, dependsOn: services },
);
return {
wsCluster: wsCluster.name,
wsConfig: wsMinimalConfig.name,
};
}
The export default function main gives us flexibility in using await inside our infrastructure code and also works as an entry point for Pulumi. The returned object will be converted to stack outputs.
First we define a GCP VPC network and subnetwork — this will keep out cluster isolated from the start and that’s where the virtual machines are going to be created in.
Then we define a WorkstationCluster itself — this creates a new control plane of Cloud Workstations that manages instances.
The WorkstationConfig is a template for creating new virtual machines. The very minimal one as we have here just specifies the cluster to work with and the location. It will use Code OSS IDE by default, provision e2-standard-4 virtual machines and use project-default compute engine service account to run the VMs.
Now all what’s left is to call pulumi up and see how your first workstations cluster is going to be created.
Adding workstations
You can now add workstations with same IaaC setup using the following snippet:
const workstation = new gcp.workstations.Workstation(
"test-workstation",
{
workstationId: "test-workstation",
workstationConfigId: wsMinimalConfig.workstationConfigId,
workstationClusterId: wsCluster.workstationClusterId,
location: region,
},
{ provider: gcpProvider, dependsOn: services },
);
Or you can as well let your engineers create workstations using Google Cloud UI.
Or usinggcloud :
gcloud workstations create test-workstation \
--cluster=test-cluster \
--config=minimal-config \
--region=us-central1
Source Credit: https://medium.com/google-cloud/cloud-workstations-building-reusable-development-environments-in-cloud-262bc0dcdb57?source=rss—-e52cf94d98af—4
