Reputation: 2219
I've been attempting to create a managed instance group on GCP which consists of instances that host a custom docker image. However I'm struggling to figure out how to do this with Pulumi.
Reading Google's GCP documentation it's possible to deploy instances that host a docker container within a managed instance group via instance templates.
Practically with gcloud this looks like:
gcloud compute instance-templates create-with-container TEMPLATE_NAME --container-image DOCKER_IMAGE
Reading Pulumi's instance template documentation however, it's not clear how to create an instance template which would do the same thing as the command above.
Is it possible in Pulumi to create a managed instance group where the instances host a custom docker image, or will I have to do something like create an instance template manually, and refer to that within my Pulumi script?
Upvotes: 1
Views: 916
Reputation: 2219
Here's a hybrid approach that utilises both gcloud and Pulumi.
At a high level:
#1 Creating the Docker Container
Use CloudBuild to detect changes within a Git repo, create a docker container, and upload it to the Google Container Registry.
Within my repo I have a Dockerfile file with instructions on how to build the container that will be used for my instance. I use Supervisord to start and monitor my application.
Here's how it looks:
# my-app-repo/Dockerfile
FROM ubuntu:22.04
RUN apt update
RUN apt -y install software-properties-common
RUN apt install -y supervisor
COPY supervisord.conf /etc/supervisord.conf
RUN chmod 0700 /etc/supervisord.conf
COPY ./my-app /home/my-app
RUN chmod u+x /home/my-app
EXPOSE 443/tcp # HTTPS
EXPOSE 9001/tcp # supervisord support
CMD ["supervisord", "-c", "/etc/supervisord.conf"]
The second part of this is to build the docker container and upload to the Google Container Registry. I do this via CloudBuild. Here's the corresponding Pulumi code (building a Golang app):
Note: make sure you've connected the repo via the CloudBuild section of the GCP website first
const myImageName = pulumi.interpolate`gcr.io/${project}/my-image-name`
const buildTrigger = new gcp.cloudbuild.Trigger("my-app-build-trigger", {
name: "my-app",
description: "Builds My App image",
build: {
steps: [
{
name: "golang",
id: "build-server",
entrypoint: "bash",
timeout: "300s",
args: ["-c", "go build"],
},
{
name: "gcr.io/cloud-builders/docker",
id: "build-docker-image",
args: [
"build",
"-t", pulumi.interpolate`${myImageName}:$BRANCH_NAME-$REVISION_ID`,
"-t", pulumi.interpolate`${myImageName}:latest`,
'.',
],
},
],
images: [myImageName]
},
github: {
name: "my-app-repo",
owner: "MyGithubUsername",
push: {
branch: "^main$"
}
},
});
#2 Creating an Instance Template
As I haven't been able to figure out how to easily create an instance template via Pulumi, I decided to use the Google SDK via the gcloud
commandline tool.
gcloud compute instance-templates create-with-container my-template-name-01 \
--region us-central1 \
--container-image=gcr.io/my-project/my-image-name:main-e286d94217719c3be79aac1cbd39c0a629b84de3 \
--machine-type=e2-micro \
--network=my-network-name-59c9c08 \
--tags=my-tag-name \
--service-account=my-service-account@my-project.iam.gserviceaccount.com
The values for above (container, network name etc) I got simply by browsing my project on the GCP website.
#3 Creating the Managed Instance Group
Having created an instance template you can now reference that template within your Pulumi script
const myHealthCheck = new gcp.compute.HealthCheck("my-app-health-check", {
checkIntervalSec: 5,
timeoutSec: 5,
healthyThreshold: 2,
unhealthyThreshold: 5,
httpHealthCheck: {
requestPath: "/health-check",
port: 80,
},
});
const instanceGroupManager = new gcp.compute.InstanceGroupManager("my-app-instance-group", {
baseInstanceName: "my-app-name-prefix",
zone: hostZone,
targetSize: 2,
versions: [
{
name: "my-app",
instanceTemplate: "https://www.googleapis.com/compute/v1/projects/my-project/global/instanceTemplates/my-template-name-01",
},
],
autoHealingPolicies: {
healthCheck: myHealthCheck.id,
initialDelaySec: 300,
},
});
For completeness, I've also included another part of my Pulumi script which creates a backend service and connects it to the instance group created above via the InstanceGroupManager call. Note that the Load Balancer in this example is using TCP instead of HTTPS (My App is handling SSL connections and thus uses a TCP Network Load Balancer).
const backendService = new gcp.compute.RegionBackendService("my-app-backend-service", {
region: hostRegion,
enableCdn: false,
protocol: "TCP",
backends: [{
group: instanceGroupManager.instanceGroup,
}],
healthChecks: defaultHttpHealthCheck.id,
loadBalancingScheme: "EXTERNAL",
});
const myForwardingRule = new gcp.compute.ForwardingRule("my-app-forwarding-rule", {
description: "HTTPS forwarding rule",
region: hostRegion,
ipAddress: myIPAddress.address,
backendService: backendService.id,
portRange: "443",
});
Note: Ideally step #2 would be done with Pulumi as well however I haven't worked that part out just yet.
Upvotes: 0