Kubernetes provider for Bicep together with user-defined types

Kubernetes provider for Bicep together with user-defined types

This article was originally posted on my blog mattias.engineer

In the?latest release?of Azure Bicep user-defined functions was introduced. That is an excellent news, but I will not be covering that in this post. I want to introduce the idea of modules being very much like user-defined functions. In the past I have done crazy things like?solving Advent of Code problems using Azure Bicep, and the most important thing I learned from that adventure was that a module is the closest thing to a function that Bicep has. This will of course change with the introduction of user-defined functions.

In this short post I want to use a Bicep module to deploy Deployment-objects in a Kubernetes cluster. So I will be using two experimental features of Bicep: the Kubernetes provider and user-defined types. I will create a Bicep module for Kubernetes deployments, and illustrate how that module can work like a function that generates deployments for you.

Bicep configuration

To be able to work with Kubernetes resources in Azure, you need to set the?experimentalFeaturesEnabled.extensibility?to?true?in?bicepconfig.json. You will also need to set?experimentalFeaturesEnabled.userDefinedTypes?to?true?to be able to use user-defined types. Your?bicepconfig.json?should look like this:

{
    "experimentalFeaturesEnabled": {
        "extensibility": true,
        "userDefinedTypes": true
    }
}        

Now we are ready to write some Bicep!

Writing the deployment module

When I write a module in Bicep I usually create a?modules?directory and put my modules there. This is true this time as well. In my modules directory I create a file called?deployment.bicep?where I will define my Kubernetes deployment module.

To start off I will create a user-defined type for my Kubernetes deployments. A deployment in Kubernetes can be complex, so to keep the complexity down a bit I will only require the deployment to have a name and an image. My custom type looks like this:

@description('Custom type for a Kubernetes deployment')
@sealed()
type deploymentConfigType = {
  name: string
  image: string
}        

I use the?@sealed()?decorator to specify that I can’t provide additional fields to parameters using this type, I will specifically require the?name?and the?image?fields. If you use this example as a starting-point for your own work then this user-defined type needs to be extended to include additional fields.

Next up I define three parameters for my module:

@description('kubeconfig to authenticate to the cluster')
param kubeConfig string

@description('Kubernetes namespace for the deployments')
param namespace string

@description('Configurations for the deployments')
param deployments deploymentConfigType[]        

The?kubeConfig?parameter is used to authenticate to the Kubernetes cluster I want to create the deployments in. The?namespace?parameter is the Kubernetes namespace where the deployments should live, this is required for the configuration of the Kubernetes provider (see below). The last parameter is?deployments?which is a list of my user-defined type?deploymentConfigType. This list will contain the definitions of all the Kubernetes deployments I want to create.

Next I need to import the Kubernetes provider:

import '[email protected]' with {
  kubeConfig: kubeConfig
  namespace: namespace
}        

Here I used the?kubeConfig?and?namespace?parameters. When I have imported the Kubernetes provider I can start creating Kubernetes resources just like I would create Azure resources! So, the last step in this module is to do just that. I create my deployment objects in Kubernetes like so:

resource deploy 'apps/Deployment@v1' = [for deployment in deployments: {
  metadata: {
    name: deployment.name
  }
  spec: {
    selector: {
      matchLabels: {
        app: deployment.name
      }
    }
    template: {
      metadata: {
        name: deployment.name
        labels: {
          app: deployment.name
        }
      }
      spec: {
        containers: [
          {
            name: 'main'
            image: deployment.image
          }
        ]
      }
    }
  }
}]        

If you are familiar with Kubernetes deployments then this will look very familiar. It is simply a Kubernetes deployment in Bicep code. As I mentioned above, it is a simplified deployment object where I only specify a few things. To summarize, the?deployment.bicep?module should look like the following:

@description('Custom type for a Kubernetes deployment')
@sealed()
type deploymentConfigType = {
  name: string
  image: string
}

@description('kubeconfig to authenticate to the cluster')
param kubeConfig string

@description('Kubernetes namespace for the deployments')
param namespace string

@description('Configurations for the deployments')
param deployments deploymentConfigType[]

import '[email protected]' with {
  kubeConfig: kubeConfig
  namespace: namespace
}

resource deploy 'apps/Deployment@v1' = [for deployment in deployments: {
  metadata: {
    name: deployment.name
  }
  spec: {
    selector: {
      matchLabels: {
        app: deployment.name
      }
    }
    template: {
      metadata: {
        name: deployment.name
        labels: {
          app: deployment.name
        }
      }
      spec: {
        containers: [
          {
            name: 'main'
            image: deployment.image
          }
        ]
      }
    }
  }
}]        

Using the module

Now it is time to use the module! I create a?main.bicep?file with the following content:

@description('kubeconfig to authenticate to the cluster')
param kubeConfig string

@description('Kubernetes namespace for the deployments')
param namespace string

var deployments = [
  {
    name: 'nginx1'
    image: 'nginx:1.23.4'
  }
  {
    name: 'nginx2'
    image: 'nginx:1.24.0'
  }
]

module deploymentModule 'modules/deployment.bicep' = {
  name: 'deployment-module'
  params: {
    deployments: deployments
    kubeConfig: kubeConfig
    namespace: namespace
  }
}        

I have the?kubeConfig?and?namespace?parameters repeated here, so I expect them to be sent into the deployment command. I define a variable?deployments?which is an array of deployment objects. Each object follows the user-defined type I have in my?deployment.bicep?file. Note that as of now (May 2023) you can’t put your user-defined types in a separate file and import them to where you need them. This is why I keep the user-defined type in?deployment.bicep?only, for now. If you use the Bicep extension in VS Code you will get a warning if you forget a certain property, or if you add a property the type does not contain.

In this case I created two Kubernetes deployments, with two different versions of nginx. This was so simplify my own life a bit, because the nginx container will start up without any need for a command. This allowed me to keep the user-defined type to a minimum, in order to not get bogged down in details about containers!

Now you are ready to run a Bicep deployment to create your Kubernetes deployments, good luck!

要查看或添加评论,请登录

Mattias Fjellstr?m的更多文章

  • Azure Flexible Federated Identity Credentials for HCP Terraform

    Azure Flexible Federated Identity Credentials for HCP Terraform

    If you set up workload identity integration between HCP Terraform and Azure today, you need to configure the subject to…

  • The new Terraform Cloud pricing model is so expensive! Or is it?

    The new Terraform Cloud pricing model is so expensive! Or is it?

    Terraform Cloud recently presented a new pricing model. Previously you paid $20 per user per month, for the team tier.

    7 条评论
  • Azure Kubernetes Service in production - Part 1 - Introduction

    Azure Kubernetes Service in production - Part 1 - Introduction

    Kubernetes can seem notoriously difficult to get right. It is! This is true even if you use a managed service from one…

    1 条评论
  • How to promote releases between GitOps environments

    How to promote releases between GitOps environments

    In this post I will go through an example of how you can promote releases between different GitOps environments. The…

    2 条评论
  • Administering Terraform Cloud using GitHub Actions

    Administering Terraform Cloud using GitHub Actions

    Note that this post is cross-posted here, at my blog mattias.engineer, and at dev.

    1 条评论
  • Kubernetes-101: 1000-Foot Overview

    Kubernetes-101: 1000-Foot Overview

    In this series of articles we have looked at many of the Kubernetes primitives that are needed to run a working…

  • Kubernetes-101: Security concepts

    Kubernetes-101: Security concepts

    Security is in general a large and complex topic, and security in Kubernetes is no different. This is especially true…

  • Kubernetes-101: Ingress

    Kubernetes-101: Ingress

    In this article we will look at the Ingress resource. An Ingress exposes a Service on HTTP or HTTPS outside of the…

    1 条评论
  • Kubernetes-101: Helm

    Kubernetes-101: Helm

    We’re getting closer to the end of this series of Kubernetes-101 articles! In the summary of this article I will list…

  • Kubernetes-101: Pods, part 3

    Kubernetes-101: Pods, part 3

    In this article we will revisit the topic of Pods. Remember Pods? Of course you do, we have been using them all along.

社区洞察

其他会员也浏览了