feat: add Apache Cloudstack support
Add support for new platform. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com> Signed-off-by: Claus Albøge <ca@netic.dk>
This commit is contained in:
parent
951cf66fdc
commit
75cecb4210
2
Makefile
2
Makefile
@ -395,7 +395,7 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc
|
||||
|
||||
images-essential: image-aws image-azure image-gcp image-metal secureboot-installer ## Builds only essential images used in the CI (AWS, GCP, and Metal).
|
||||
|
||||
images: image-akamai image-aws image-azure image-digital-ocean image-exoscale image-gcp image-hcloud image-iso image-metal image-nocloud image-opennebula image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, Exoscale, GCP, HCloud, Metal, NoCloud, OpenNebula, OpenStack, Oracle, Scaleway, UpCloud, Vultr and VMware).
|
||||
images: image-akamai image-aws image-azure image-digital-ocean image-exoscale image-cloudstack image-gcp image-hcloud image-iso image-metal image-nocloud image-opennebula image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr ## Builds all known images (AWS, Azure, DigitalOcean, Exoscale, Cloudstack, GCP, HCloud, Metal, NoCloud, OpenNebula, OpenStack, Oracle, Scaleway, UpCloud, Vultr and VMware).
|
||||
|
||||
.PHONY: iso
|
||||
iso: image-iso ## Builds the ISO and outputs it to the artifact directory.
|
||||
|
@ -192,6 +192,12 @@ The Talos bundled Flannel manifest was simplified to remove the `install-cni` st
|
||||
title = "Device Extra Settle Timeout"
|
||||
description = """\
|
||||
Talos Linux now supports a kernel command line argument `talos.device.settle_time=3m` to set the device extra settle timeout to workaround issues with broken drivers.
|
||||
"""
|
||||
|
||||
[notes.platform]
|
||||
title = "Platform Support"
|
||||
description = """\
|
||||
Talos Linux now supports Apache CloudStack platform.
|
||||
"""
|
||||
|
||||
[make_deps]
|
||||
|
@ -0,0 +1,117 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package cloudstack contains the Cloudstack platform implementation.
|
||||
package cloudstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/siderolabs/go-procfs/procfs"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils"
|
||||
"github.com/siderolabs/talos/pkg/download"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||
)
|
||||
|
||||
// Cloudstack is the concrete type that implements the runtime.Platform interface.
|
||||
type Cloudstack struct{}
|
||||
|
||||
// ParseMetadata converts Cloudstack platform metadata into platform network config.
|
||||
func (e *Cloudstack) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) {
|
||||
networkConfig := &runtime.PlatformNetworkConfig{}
|
||||
|
||||
if metadata.Hostname != "" {
|
||||
hostnameSpec := network.HostnameSpecSpec{
|
||||
ConfigLayer: network.ConfigPlatform,
|
||||
}
|
||||
|
||||
if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec)
|
||||
}
|
||||
|
||||
if metadata.PublicIPv4 != "" {
|
||||
if ip, err := netip.ParseAddr(metadata.PublicIPv4); err == nil {
|
||||
networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{
|
||||
Platform: e.Name(),
|
||||
Hostname: metadata.Hostname,
|
||||
Region: metadata.Zone,
|
||||
Zone: metadata.Zone,
|
||||
InstanceType: strings.ToLower(strings.SplitN(metadata.InstanceType, " ", 2)[0]),
|
||||
InstanceID: metadata.InstanceID,
|
||||
ProviderID: fmt.Sprintf("cloudstack://%s", metadata.InstanceID),
|
||||
}
|
||||
|
||||
return networkConfig, nil
|
||||
}
|
||||
|
||||
// Name implements the runtime.Platform interface.
|
||||
func (e *Cloudstack) Name() string {
|
||||
return "cloudstack"
|
||||
}
|
||||
|
||||
// Configuration implements the runtime.Platform interface.
|
||||
func (e *Cloudstack) Configuration(ctx context.Context, r state.State) ([]byte, error) {
|
||||
if err := netutils.Wait(ctx, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("fetching machine config from %q", CloudstackUserDataEndpoint)
|
||||
|
||||
return download.Download(ctx, CloudstackUserDataEndpoint,
|
||||
download.WithErrorOnNotFound(errors.ErrNoConfigSource),
|
||||
download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource))
|
||||
}
|
||||
|
||||
// Mode implements the runtime.Platform interface.
|
||||
func (e *Cloudstack) Mode() runtime.Mode {
|
||||
return runtime.ModeCloud
|
||||
}
|
||||
|
||||
// KernelArgs implements the runtime.Platform interface.
|
||||
func (e *Cloudstack) KernelArgs(string) procfs.Parameters {
|
||||
return []*procfs.Parameter{
|
||||
procfs.NewParameter("console").Append("tty1").Append("ttyS0"),
|
||||
procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"),
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkConfiguration implements the runtime.Platform interface.
|
||||
func (e *Cloudstack) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error {
|
||||
log.Printf("fetching cloudstack instance config from: %q", CloudstackMetadataEndpoint)
|
||||
|
||||
metadata, err := e.getMetadata(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkConfig, err := e.ParseMetadata(metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- networkConfig:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package cloudstack_test
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack"
|
||||
)
|
||||
|
||||
//go:embed testdata/metadata.json
|
||||
var rawMetadata []byte
|
||||
|
||||
//go:embed testdata/expected.yaml
|
||||
var expectedNetworkConfig string
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
p := &cloudstack.Cloudstack{}
|
||||
|
||||
var m cloudstack.MetadataConfig
|
||||
|
||||
require.NoError(t, json.Unmarshal(rawMetadata, &m))
|
||||
|
||||
networkConfig, err := p.ParseMetadata(&m)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshaled, err := yaml.Marshal(networkConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedNetworkConfig, string(marshaled))
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package cloudstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
|
||||
"github.com/siderolabs/talos/pkg/download"
|
||||
)
|
||||
|
||||
const (
|
||||
// CloudstackMetadataEndpoint is the local Cloudstack endpoint.
|
||||
CloudstackMetadataEndpoint = "http://data-server./latest/meta-data"
|
||||
// CloudstackUserDataEndpoint is the local Cloudstack endpoint for the config.
|
||||
CloudstackUserDataEndpoint = "http://data-server./latest/user-data"
|
||||
)
|
||||
|
||||
// MetadataConfig represents a metadata Cloudstack instance.
|
||||
type MetadataConfig struct {
|
||||
Hostname string `json:"local-hostname,omitempty"`
|
||||
InstanceID string `json:"instance-id,omitempty"`
|
||||
InstanceType string `json:"service-offering,omitempty"`
|
||||
PublicIPv4 string `json:"public-ipv4,omitempty"`
|
||||
Zone string `json:"availability-zone,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
local-ipv4
|
||||
public-hostname
|
||||
vm-id
|
||||
public-keys
|
||||
cloud-identifier
|
||||
hypervisor-host-name
|
||||
*/
|
||||
|
||||
func (e *Cloudstack) getMetadata(ctx context.Context) (metadata *MetadataConfig, err error) {
|
||||
getMetadataKey := func(key string) (string, error) {
|
||||
res, metaerr := download.Download(ctx, fmt.Sprintf("%s/%s", CloudstackMetadataEndpoint, key),
|
||||
download.WithErrorOnNotFound(errors.ErrNoConfigSource),
|
||||
download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource))
|
||||
if metaerr != nil && !stderrors.Is(metaerr, errors.ErrNoConfigSource) {
|
||||
return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, metaerr)
|
||||
}
|
||||
|
||||
return string(res), nil
|
||||
}
|
||||
|
||||
metadata = &MetadataConfig{}
|
||||
|
||||
if metadata.Hostname, err = getMetadataKey("local-hostname"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.InstanceType, err = getMetadataKey("service-offering"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if metadata.Zone, err = getMetadataKey("availability-zone"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
18
internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml
vendored
Normal file
18
internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
addresses: []
|
||||
links: []
|
||||
routes: []
|
||||
hostnames:
|
||||
- hostname: talos
|
||||
domainname: fqdn
|
||||
layer: platform
|
||||
resolvers: []
|
||||
timeServers: []
|
||||
operators: []
|
||||
externalIPs:
|
||||
- 1.2.3.4
|
||||
metadata:
|
||||
platform: cloudstack
|
||||
hostname: talos.fqdn
|
||||
instanceType: standard.tiny
|
||||
instanceId: 3fe6b28a-669e-4eb2-bffd-4180c572c410
|
||||
providerId: cloudstack://3fe6b28a-669e-4eb2-bffd-4180c572c410
|
6
internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json
vendored
Normal file
6
internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"local-hostname": "talos.fqdn",
|
||||
"instance-id": "3fe6b28a-669e-4eb2-bffd-4180c572c410",
|
||||
"public-ipv4": "1.2.3.4",
|
||||
"service-offering": "standard.tiny"
|
||||
}
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/akamai"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/aws"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/azure"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal"
|
||||
@ -88,7 +89,7 @@ func NewPlatform(platform string) (p runtime.Platform, err error) {
|
||||
return newPlatform(platform)
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
//nolint:gocyclo,cyclop
|
||||
func newPlatform(platform string) (p runtime.Platform, err error) {
|
||||
switch platform {
|
||||
case "akamai":
|
||||
@ -97,6 +98,8 @@ func newPlatform(platform string) (p runtime.Platform, err error) {
|
||||
return aws.NewAWS()
|
||||
case "azure":
|
||||
p = &azure.Azure{}
|
||||
case "cloudstack":
|
||||
p = &cloudstack.Cloudstack{}
|
||||
case "container":
|
||||
p = &container.Container{}
|
||||
case "digital-ocean":
|
||||
|
@ -121,6 +121,18 @@ var Default = map[string]Profile{
|
||||
},
|
||||
},
|
||||
},
|
||||
"cloudstack": {
|
||||
Platform: "cloudstack",
|
||||
SecureBoot: pointer.To(false),
|
||||
Output: Output{
|
||||
Kind: OutKindImage,
|
||||
OutFormat: OutFormatZSTD,
|
||||
ImageOptions: &ImageOptions{
|
||||
DiskSize: DefaultRAWDiskSize,
|
||||
DiskFormat: DiskFormatRaw,
|
||||
},
|
||||
},
|
||||
},
|
||||
"digital-ocean": {
|
||||
Platform: "digital-ocean",
|
||||
SecureBoot: pointer.To(false),
|
||||
|
@ -6,14 +6,14 @@ description: "Table of supported Talos Linux versions and respective platforms."
|
||||
|
||||
| Talos Version | 1.8 | 1.7 |
|
||||
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release Date | 2024-08-15 (TBD) | 2024-04-19 (1.7.0) |
|
||||
| Release Date | 2024-09-15 (TBD) | 2024-04-19 (1.7.0) |
|
||||
| End of Community Support | 1.9.0 release (2024-12-15, TBD) | 1.8.0 release (2024-08-15) |
|
||||
| Enterprise Support | [offered by Sidero Labs Inc.](https://www.siderolabs.com/support/) | [offered by Sidero Labs Inc.](https://www.siderolabs.com/support/) |
|
||||
| Kubernetes | 1.31, 1.30, 1.29, 1.28, 1.27, 1.26 | 1.30, 1.29, 1.28, 1.27, 1.26, 1.25 |
|
||||
| NVIDIA Drivers | 550.x.x (PRODUCTION), 535.x.x (LTS) | 535.x.x (LTS) |
|
||||
| Architecture | amd64, arm64 | amd64, arm64 |
|
||||
| **Platforms** | | |
|
||||
| - cloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud |
|
||||
| - cloud | Akamai, AWS, GCP, Azure, CloudStack, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud | Akamai, AWS, GCP, Azure, Digital Ocean, Exoscale, Hetzner, OpenNebula, OpenStack, Oracle Cloud, Scaleway, Vultr, Upcloud |
|
||||
| - bare metal | x86: BIOS, UEFI, SecureBoot; arm64: UEFI, SecureBoot; boot: ISO, PXE, disk image | x86: BIOS, UEFI; arm64: UEFI; boot: ISO, PXE, disk image |
|
||||
| - virtualized | VMware, Hyper-V, KVM, Proxmox, Xen | VMware, Hyper-V, KVM, Proxmox, Xen |
|
||||
| - SBCs | Banana Pi M64, Jetson Nano, Libre Computer Board ALL-H3-CC, Nano Pi R4S, Pine64, Pine64 Rock64, Radxa ROCK Pi 4c, Radxa Rock4c+, Raspberry Pi 4B, Raspberry Pi Compute Module 4 | Banana Pi M64, Jetson Nano, Libre Computer Board ALL-H3-CC, Nano Pi R4S, Orange Pi R1 Plus LTS, Pine64, Pine64 Rock64, Radxa ROCK Pi 4c, Raspberry Pi 4B, Raspberry Pi Compute Module 4 |
|
||||
@ -45,6 +45,7 @@ description: "Table of supported Talos Linux versions and respective platforms."
|
||||
### Tier 3
|
||||
|
||||
* Akamai
|
||||
* CloudStack
|
||||
* Exoscale
|
||||
* Hetzner
|
||||
* nocloud
|
||||
|
@ -0,0 +1,228 @@
|
||||
---
|
||||
title: "CloudStack"
|
||||
description: "Creating a cluster via the CLI (cmk) on Apache CloudStack."
|
||||
---
|
||||
|
||||
## Creating a Talos Linux Cluster on Apache CloudStack via the CMK CLI
|
||||
|
||||
In this guide we will create an single node Kubernetes cluster in Apache CloudStack.
|
||||
|
||||
We assume Apache CloudStack is already running in a basic configuration - and some familiarity with Apache CloudStack.
|
||||
|
||||
We will be using the [CloudStack Cloudmonkey](https://github.com/apache/cloudstack-cloudmonkey) CLI tool.
|
||||
|
||||
Please see the [official Apache CloudStack documentation](https://docs.cloudstack.apache.org/en/latest/) for information related to Apache CloudStack.
|
||||
|
||||
### Obtain the Talos Image
|
||||
|
||||
Download the Talos CloudStack image `cloudstack-amd64.raw.gz` from the [Image Factory](https://factory.talos.dev).
|
||||
|
||||
> Note: the minimum version of Talos required to support Apache CloudStack is v1.8.0.
|
||||
|
||||
Using an upload method of your choice, upload the image to a Apache CloudStack.
|
||||
|
||||
You might be able to use the "Register Template from URL" to download the image directly from the Image Factory.
|
||||
|
||||
> Note: CloudStack does not seem to like compressed images, so you might have to download the image to a local webserver, uncompress it and let CloudStack fetch the image from there instead.
|
||||
> Alternatively, you can try to remove `.gz` from URL to fetch an uncompressed image from the Image Factory.
|
||||
|
||||
### Get Required Variables
|
||||
|
||||
Next we will get a number of required variables and export them for later use:
|
||||
|
||||
#### Get Image Template ID
|
||||
|
||||
```bash
|
||||
$ cmk list templates templatefilter=self | jq -r '.template[] | [.id, .name] | @tsv' | sort -k2
|
||||
01813d29-1253-4080-8d29-d405d94148af Talos 1.8.0
|
||||
...
|
||||
$ export IMAGE_ID=01813d29-1253-4080-8d29-d405d94148af
|
||||
```
|
||||
|
||||
#### Get Zone ID
|
||||
|
||||
Get a list of Zones and select the relevant zone
|
||||
|
||||
```bash
|
||||
$ cmk list zones | jq -r '.zone[] | [.id, .name] | @tsv' | sort -k2
|
||||
a8c71a6f-2e09-41ed-8754-2d4dd8783920 fsn1
|
||||
9d38497b-d810-42ab-a772-e596994d21d2 fsn2
|
||||
...
|
||||
$ export ZONE_ID=a8c71a6f-2e09-41ed-8754-2d4dd8783920
|
||||
```
|
||||
|
||||
#### Get Service Offering ID
|
||||
|
||||
Get a list of service offerings (instance types) and select the desired offering
|
||||
|
||||
```bash
|
||||
$ cmk list serviceofferings | jq -r '.serviceoffering[] | [.id, .memory, .cpunumber, .name] | @tsv' | sort -k4
|
||||
82ac8c87-22ee-4ec3-8003-c80b09efe02c 2048 2 K8S-CP-S
|
||||
c7f5253e-e1f1-4e33-a45e-eb2ebbc65fd4 4096 2 K8S-WRK-S
|
||||
...
|
||||
$ export SERVICEOFFERING_ID=82ac8c87-22ee-4ec3-8003-c80b09efe02c
|
||||
```
|
||||
|
||||
#### Get Network ID
|
||||
|
||||
Get a list of networks and select the relevant network for your cluster.
|
||||
|
||||
```bash
|
||||
$ cmk list networks zoneid=${ZONE_ID} | jq -r '.network[] | [.id, .type, .name] | @tsv' | sort -k3
|
||||
f706984f-9dd1-4cb8-9493-3fba1f0de7e3 Isolate demo
|
||||
143ed8f1-3cc5-4ba2-8717-457ad993cf25 Isolated talos
|
||||
...
|
||||
$ export NETWORK_ID=143ed8f1-3cc5-4ba2-8717-457ad993cf25
|
||||
```
|
||||
|
||||
#### Get next free Public IP address and ID
|
||||
|
||||
To create a loadbalancer for the K8S API Endpoint, find the next available public IP address in the zone.
|
||||
|
||||
(In this test environment, the 10.0.0.0/24 RFC-1918 IP range has been configured as "Public IP addresses")
|
||||
|
||||
```bash
|
||||
$ cmk list publicipaddresses zoneid=${ZONE_ID} state=free forvirtualnetwork=true | jq -r '.publicipaddress[] | [.id, .ipaddress] | @tsv' | sort -k2
|
||||
1901d946-3797-48aa-a113-8fb730b0770a 10.0.0.102
|
||||
fa207d0e-c8f8-4f09-80f0-d45a6aac77eb 10.0.0.103
|
||||
aa397291-f5dc-4903-b299-277161b406cb 10.0.0.104
|
||||
...
|
||||
$ export PUBLIC_IPADDRESS=10.0.0.102
|
||||
$ export PUBLIC_IPADDRESS_ID=1901d946-3797-48aa-a113-8fb730b0770a
|
||||
```
|
||||
|
||||
#### Acquire and Associate Public IP Address
|
||||
|
||||
Acquire and associate the public IP address with the network we selected earlier.
|
||||
|
||||
```bash
|
||||
$ cmk associateIpAddress ipaddress=${PUBLIC_IPADDRESS} networkid=${NETWORK_ID}
|
||||
{
|
||||
"ipaddress": {
|
||||
...,
|
||||
"ipaddress": "10.0.0.102",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Create LB and FW rule using the Public IP Address
|
||||
|
||||
Create a Loadbalancer for the K8S API Endpoint.
|
||||
|
||||
> Note: The "create loadbalancerrule" also takes care of creating a corresponding firewallrule.
|
||||
|
||||
```bash
|
||||
$ cmk create loadbalancerrule algorithm=roundrobin name="k8s-api" privateport=6443 publicport=6443 openfirewall=true publicipid=${PUBLIC_IPADDRESS_ID} cidrlist=0.0.0.0/0
|
||||
{
|
||||
"loadbalancer": {
|
||||
...
|
||||
"name": "k8s-api",
|
||||
"networkid": "143ed8f1-3cc5-4ba2-8717-457ad993cf25",
|
||||
"privateport": "6443",
|
||||
"publicip": "10.0.0.102",
|
||||
"publicipid": "1901d946-3797-48aa-a113-8fb730b0770a",
|
||||
"publicport": "6443",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create the Talos Configuration Files
|
||||
|
||||
Finally it's time to generate the Talos configuration files, using the Public IP address assigned to the loadbalancer.
|
||||
|
||||
```bash
|
||||
$ talosctl gen config talos-cloudstack https://${PUBLIC_IPADDRESS}:6443 --with-docs=false --with-examples=false
|
||||
created controlplane.yaml
|
||||
created worker.yaml
|
||||
created talosconfig
|
||||
```
|
||||
|
||||
Make any adjustments to the `controlplane.yaml` and/or `worker.yaml` as you like.
|
||||
|
||||
> Note: Remember to validate!
|
||||
|
||||
#### Create Talos VM
|
||||
|
||||
Next we will create the actual VM and supply the `controlplane.yaml` as base64 encoded `userdata`.
|
||||
|
||||
```bash
|
||||
$ cmk deploy virtualmachine zoneid=${ZONEID} templateid=${IMAGE_ID} serviceofferingid=${SERVICEOFFERING_ID} networkIds=${NETWORK_ID} name=talosdemo usersdata=$(base64 controlplane.yaml | tr -d '\n')
|
||||
{
|
||||
"virtualmachine": {
|
||||
"account": "admin",
|
||||
"affinitygroup": [],
|
||||
"cpunumber": 2,
|
||||
"cpuspeed": 2000,
|
||||
"cpuused": "0.3%",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Get Talos VM ID and Internal IP address
|
||||
|
||||
Get the ID of our newly created VM.
|
||||
(Also available in the full output of the above command.)
|
||||
|
||||
```bash
|
||||
$ cmk list virtualmachines | jq -r '.virtualmachine[] | [.id, .ipaddress, .name]|@tsv' | sort -k3
|
||||
9c119627-cb38-4b64-876b-ca2b79820b5a 10.1.1.154 srv03
|
||||
545099fc-ec2d-4f32-915d-b0c821cfb634 10.1.1.97 srv04
|
||||
d37aeca4-7d1f-45cd-9a4d-97fdbf535aa1 10.1.1.243 talosdemo
|
||||
$ export VM_ID=d37aeca4-7d1f-45cd-9a4d-97fdbf535aa1
|
||||
$ export VM_IP=10.1.1.243
|
||||
```
|
||||
|
||||
#### Get Load Balancer ID
|
||||
|
||||
Obtain the ID of the `loadbalancerrule` we created earlier.
|
||||
|
||||
```bash
|
||||
$ cmk list loadbalancerrules | jq -r '.loadbalancerrule[]| [.id, .publicip, .name] | @tsv' | sort -k2
|
||||
ede6b711-b6bc-4ade-9e48-4b3f5aa59934 10.0.0.102 k8s-api
|
||||
1bad3c46-96fa-4f50-a4fc-9a46a54bc350 10.0.0.197 ac0b5d98cf6a24d55a4fb2f9e240c473-tcp-443
|
||||
$ export LB_RULE_ID=ede6b711-b6bc-4ade-9e48-4b3f5aa59934
|
||||
```
|
||||
|
||||
#### Assign Talos VM to Load Balancer
|
||||
|
||||
With the ID of the VM and the load balancer, we can assign the VM to the `loadbalancerrule`, making the K8S API endpoint available via the Load Balancer
|
||||
|
||||
```bash
|
||||
cmk assigntoloadbalancerrule id=${LB_RULE_ID} virtualmachineids=${VM_ID}
|
||||
```
|
||||
|
||||
### Bootstrap Etcd
|
||||
|
||||
Once the Talos VM has booted, it time to bootstrap etcd.
|
||||
|
||||
Configure `talosctl` with IP addresses of the control plane node's IP address.
|
||||
|
||||
Set the `endpoints` and `nodes`:
|
||||
|
||||
```bash
|
||||
talosctl --talosconfig talosconfig config endpoint ${VM_IP}
|
||||
talosctl --talosconfig talosconfig config node ${VM_IP}
|
||||
```
|
||||
|
||||
Next, bootstrap `etcd`:
|
||||
|
||||
```bash
|
||||
talosctl --talosconfig talosconfig bootstrap
|
||||
```
|
||||
|
||||
### Retrieve the `kubeconfig`
|
||||
|
||||
At this point we can retrieve the admin `kubeconfig` by running:
|
||||
|
||||
```bash
|
||||
talosctl --talosconfig talosconfig kubeconfig .
|
||||
```
|
||||
|
||||
We can also watch the cluster bootstrap via:
|
||||
|
||||
```bash
|
||||
talosctl --talosconfig talosconfig dashboard
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user