mirror of
https://codeberg.org/scottslowe/learning-tools.git
synced 2026-03-11 09:04:37 +00:00
Add SSH-based Docker provider example
Add Go program illustrating use of an SSH-based Docker provider Signed-off-by: Scott Lowe <scott.lowe@scottlowe.org>
This commit is contained in:
parent
70ad90ccfd
commit
7ba159ad01
4 changed files with 335 additions and 0 deletions
3
docker/docker-ssh-pulumi/Pulumi.yaml
Normal file
3
docker/docker-ssh-pulumi/Pulumi.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
name: docker-ssh-pulumi
|
||||
runtime: go
|
||||
description: A Go Pulumi program to use SSH with a remote Docker host on AWS
|
||||
49
docker/docker-ssh-pulumi/README.md
Normal file
49
docker/docker-ssh-pulumi/README.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Remote Docker Host via SSH on AWS Using Pulumi
|
||||
|
||||
These files were created to demonstrate how to create a [Pulumi](https://www.pulumi.com) Docker provider that connects via SSH to a remote [Flatcar Linux](https://www.flatcar.org/) EC2 instance on AWS. This Pulumi program was written in [Go](https://go.dev).
|
||||
|
||||
Based on the "Flatcar Linux on AWS Using Pulumi" code, the Pulumi program here illustrates a couple useful patterns:
|
||||
|
||||
* Use of the `slices.Contains()` method for checking configuration values passed in from the user
|
||||
* Using [the Pulumiverse Time provider](https://www.pulumi.com/registry/packages/time/) to introduce a delay in resource creation (allowing the EC2 instance to become ready)
|
||||
* Configuring an explicit Docker provider to use SSH
|
||||
|
||||
## Contents
|
||||
|
||||
* `go.mod`: This file contains dependencies used by this Go program.
|
||||
|
||||
* `go.sum`: This file contains checksums for each of the direct and indirect dependencies. The checksum is used to validate that none of them has been modified.
|
||||
|
||||
* `main.go`: This Go file is the Pulumi program executed by the `pulumi` CLI, and contains the resource definitions to create a VPC with only public subnets, a security group to allow SSH access, and a Flatcar Linux-based EC2 instance in one of the public subnets.
|
||||
|
||||
* `Pulumi.yaml`: This is the Pulumi project file.
|
||||
|
||||
* `README.md`: This file you're currently reading.
|
||||
|
||||
## Instructions
|
||||
|
||||
These instructions assume you've already installed and configured Pulumi and all necessary dependencies (Go, for this example). Please refer to the Pulumi documentation for more details on installation or configuration.
|
||||
|
||||
1. Copy the contents of this directory down to a directory on your system, or clone the entire repository and then change into the directory where this section of the cloned repository resides.
|
||||
|
||||
1. Run `pulumi stack init` to create a new stack.
|
||||
|
||||
1. Run `pulumi config set aws:region <region-name>` to set the AWS region where the Pulumi program should create resources. _This is a required configuration value; CLI operations will fail if you don't set this value._
|
||||
|
||||
1. Run `pulumi config set keypair <name-of-aws-keypair>` to set the name of the AWS keypair that should be used. _This is a required configuration value._
|
||||
|
||||
1. Run `pulumi config set privatekeyfile <path-to-private-key-file>` to set the name of the private key file for the AWS keypair specified in the previous step. _This is a required configuration value._
|
||||
|
||||
1. (Optional) Run `pulumi config set` to set configuration values that affect the behavior of the Pulumi program. The optional configuration values are:
|
||||
|
||||
* `architecture`: Set this to "amd64" or "arm64". The values "x86_64" and "x64" are also supported and will have the same effect as "amd64". The default value is "arm64".
|
||||
* `networkcidr`: Set this to control the CIDR that will be used when the VPC is created. The default value is "10.0.0.0/16".
|
||||
* `channel`: Set this to "stable", "alpha", "beta", or "lts" to control the release channel for the Flatcar Linux instance. The default value is "stable".
|
||||
|
||||
1. Run `pulumi up` to instantiate the resources. The Pulumi program will create the Flatcar Linux EC2 instance, then use the Docker provider to pull down an image and launch a container---all remotely via SSH.
|
||||
|
||||
Enjoy! When you're finished, run `pulumi destroy` to tear down all the provisioned resources.
|
||||
|
||||
## License
|
||||
|
||||
This content is licensed under the MIT License.
|
||||
95
docker/docker-ssh-pulumi/go.mod
Normal file
95
docker/docker-ssh-pulumi/go.mod
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
module docker-ssh-pulumi
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/pulumi/pulumi-aws/sdk/v6 v6.18.1
|
||||
github.com/pulumi/pulumi-awsx/sdk/v2 v2.1.1
|
||||
github.com/pulumi/pulumi-docker/sdk/v4 v4.5.1
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.101.1
|
||||
github.com/pulumiverse/pulumi-time/sdk v0.0.0-20231010123146-089d7304da13
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/charmbracelet/bubbles v0.16.1 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.24.2 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.7.1 // indirect
|
||||
github.com/cheggaaa/pb v1.0.29 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/djherbis/times v1.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-git/go-git/v5 v5.11.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/opentracing/basictracer-go v1.1.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pgavlin/fx v0.1.6 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/term v1.1.0 // indirect
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
|
||||
github.com/pulumi/esc v0.6.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.1 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/texttheater/golang-levenshtein v1.0.1 // indirect
|
||||
github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/zclconf/go-cty v1.13.2 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sync v0.5.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d // indirect
|
||||
google.golang.org/grpc v1.57.1 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/frand v1.4.2 // indirect
|
||||
)
|
||||
188
docker/docker-ssh-pulumi/main.go
Normal file
188
docker/docker-ssh-pulumi/main.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"slices"
|
||||
|
||||
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2"
|
||||
awsx "github.com/pulumi/pulumi-awsx/sdk/v2/go/awsx/ec2"
|
||||
"github.com/pulumi/pulumi-docker/sdk/v4/go/docker"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
|
||||
"github.com/pulumiverse/pulumi-time/sdk/go/time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pulumi.Run(func(ctx *pulumi.Context) error {
|
||||
// Set up maps that are used later
|
||||
channelNames := []string{"stable", "beta", "alpha", "lts"}
|
||||
typeMap := map[string]string{"amd64": "t3a.small", "arm64": "t4g.small", "x86_64": "t3a.small", "x64": "t3a.small"}
|
||||
|
||||
// Retrieve configuration values
|
||||
instanceCpuArch, err := config.Try(ctx, "architecture")
|
||||
if err != nil {
|
||||
instanceCpuArch = "arm64"
|
||||
}
|
||||
instanceType, ok := typeMap[instanceCpuArch]
|
||||
if !ok {
|
||||
instanceCpuArch = "arm64"
|
||||
instanceType = "t4g.small"
|
||||
}
|
||||
if instanceCpuArch == "x86_64" || instanceCpuArch == "x64" {
|
||||
instanceCpuArch = "amd64"
|
||||
}
|
||||
vpcNetworkCidr, err := config.Try(ctx, "networkcidr")
|
||||
if err != nil {
|
||||
vpcNetworkCidr = "10.0.0.0/16"
|
||||
}
|
||||
channel, err := config.Try(ctx, "channel")
|
||||
if err != nil {
|
||||
channel = "stable"
|
||||
}
|
||||
if !slices.Contains(channelNames, channel) {
|
||||
channel = "stable"
|
||||
}
|
||||
userSuppliedKeyPair := config.Require(ctx, "keypair")
|
||||
userSuppliedPrivateKeyFile := config.Require(ctx, "privatekeyfile")
|
||||
|
||||
// Create a new VPC, subnets, and associated infrastructure
|
||||
dockerVpc, err := awsx.NewVpc(ctx, "docker-vpc", &awsx.VpcArgs{
|
||||
CidrBlock: &vpcNetworkCidr,
|
||||
EnableDnsHostnames: pulumi.Bool(true),
|
||||
EnableDnsSupport: pulumi.Bool(true),
|
||||
NatGateways: &awsx.NatGatewayConfigurationArgs{
|
||||
Strategy: awsx.NatGatewayStrategyNone,
|
||||
},
|
||||
SubnetSpecs: []awsx.SubnetSpecArgs{
|
||||
{
|
||||
Type: awsx.SubnetTypePublic,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("error creating VPC: %s", err.Error())
|
||||
}
|
||||
|
||||
// Create a Security Group that we can use to connect to our instance
|
||||
dockerSg, err := ec2.NewSecurityGroup(ctx, "docker-sg", &ec2.SecurityGroupArgs{
|
||||
VpcId: dockerVpc.VpcId,
|
||||
Egress: ec2.SecurityGroupEgressArray{
|
||||
ec2.SecurityGroupEgressArgs{
|
||||
Protocol: pulumi.String("-1"),
|
||||
FromPort: pulumi.Int(0),
|
||||
ToPort: pulumi.Int(0),
|
||||
CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
|
||||
},
|
||||
},
|
||||
Ingress: ec2.SecurityGroupIngressArray{
|
||||
ec2.SecurityGroupIngressArgs{
|
||||
Protocol: pulumi.String("tcp"),
|
||||
FromPort: pulumi.Int(22),
|
||||
ToPort: pulumi.Int(22),
|
||||
CidrBlocks: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
|
||||
},
|
||||
ec2.SecurityGroupIngressArgs{
|
||||
Protocol: pulumi.String("tcp"),
|
||||
FromPort: pulumi.Int(2379),
|
||||
ToPort: pulumi.Int(2380),
|
||||
Description: pulumi.String("Allow etcd traffic from this security group"),
|
||||
Self: pulumi.Bool(true),
|
||||
},
|
||||
ec2.SecurityGroupIngressArgs{
|
||||
Protocol: pulumi.String("tcp"),
|
||||
FromPort: pulumi.Int(4001),
|
||||
ToPort: pulumi.Int(4001),
|
||||
Description: pulumi.String("Allow etcd traffic from this security group"),
|
||||
Self: pulumi.Bool(true),
|
||||
},
|
||||
ec2.SecurityGroupIngressArgs{
|
||||
Protocol: pulumi.String("tcp"),
|
||||
FromPort: pulumi.Int(7001),
|
||||
ToPort: pulumi.Int(7001),
|
||||
Description: pulumi.String("Allow etcd traffic from this security group"),
|
||||
Self: pulumi.Bool(true),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("error creating security group: %s", err.Error())
|
||||
}
|
||||
|
||||
// Get AMI ID for Flatcar Linux instance
|
||||
amiName := fmt.Sprintf("Flatcar-%s-*", channel)
|
||||
flatcarAmi, err := ec2.LookupAmi(ctx, &ec2.LookupAmiArgs{
|
||||
Owners: []string{"075585003325"},
|
||||
MostRecent: pulumi.BoolRef(true),
|
||||
Filters: []ec2.GetAmiFilter{
|
||||
{Name: "name", Values: []string{amiName}},
|
||||
{Name: "root-device-type", Values: []string{"ebs"}},
|
||||
{Name: "virtualization-type", Values: []string{"hvm"}},
|
||||
{Name: "architecture", Values: []string{instanceCpuArch}},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("error looking up AMI: %s", err.Error())
|
||||
}
|
||||
|
||||
// Launch an instance using Flatcar Linux AMI
|
||||
flatcarInstance, err := ec2.NewInstance(ctx, "flatcar-instance", &ec2.InstanceArgs{
|
||||
Ami: pulumi.String(flatcarAmi.Id),
|
||||
InstanceType: pulumi.String(instanceType),
|
||||
AssociatePublicIpAddress: pulumi.Bool(true),
|
||||
KeyName: pulumi.StringPtr(userSuppliedKeyPair),
|
||||
SubnetId: dockerVpc.PublicSubnetIds.Index(pulumi.Int(0)),
|
||||
VpcSecurityGroupIds: pulumi.StringArray{dockerSg.ID()},
|
||||
Tags: pulumi.StringMap{
|
||||
"Name": pulumi.String("flatcar-instance"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("error launching instance: %s", err.Error())
|
||||
}
|
||||
|
||||
// Sleep for 15 seconds to allow instance to boot
|
||||
instanceBootDelay, err := time.NewSleep(ctx, "instance-boot-delay", &time.SleepArgs{
|
||||
CreateDuration: pulumi.String("20s"),
|
||||
}, pulumi.DependsOn([]pulumi.Resource{flatcarInstance}))
|
||||
if err != nil {
|
||||
log.Printf("error generating delay: %s", err.Error())
|
||||
}
|
||||
|
||||
// Create a new Docker provider
|
||||
remoteDocker, err := docker.NewProvider(ctx, "remote-docker", &docker.ProviderArgs{
|
||||
Host: pulumi.Sprintf("ssh://core@%s", flatcarInstance.PublicIp),
|
||||
SshOpts: pulumi.StringArray{
|
||||
pulumi.String("-i"), pulumi.String(userSuppliedPrivateKeyFile),
|
||||
pulumi.String("-o"), pulumi.String("StrictHostKeyChecking=no"),
|
||||
pulumi.String("-o"), pulumi.String("UserKnownHostsFile=/dev/null"),
|
||||
},
|
||||
}, pulumi.DependsOn([]pulumi.Resource{instanceBootDelay}))
|
||||
if err != nil {
|
||||
log.Printf("error creating provider: %s", err.Error())
|
||||
}
|
||||
|
||||
// Pull down a container image on the remote host
|
||||
nginxImage, err := docker.NewRemoteImage(ctx, "nginx-image", &docker.RemoteImageArgs{
|
||||
Name: pulumi.String("nginx:1.17.4-alpine"),
|
||||
}, pulumi.Provider(remoteDocker))
|
||||
if err != nil {
|
||||
log.Printf("error pulling remote image: %s", err.Error())
|
||||
}
|
||||
|
||||
// Launch a container on the remote host
|
||||
_, err = docker.NewContainer(ctx, "nginx-container", &docker.ContainerArgs{
|
||||
Image: nginxImage.ImageId,
|
||||
}, pulumi.Provider(remoteDocker))
|
||||
if err != nil {
|
||||
log.Printf("error creating remote container: %s", err.Error())
|
||||
}
|
||||
|
||||
// Export some values as stack outputs
|
||||
ctx.Export("instanceId", flatcarInstance.ID())
|
||||
ctx.Export("instancePublicIpAddress", flatcarInstance.PublicIp)
|
||||
ctx.Export("instancePrivateIpAddress", flatcarInstance.PrivateIp)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Loading…
Reference in a new issue