[Yandex Cloud documentation](../../index.md) > [Yandex Managed Service for Kubernetes](../index.md) > [Tutorials](index.md) > Creating a new Kubernetes project in Yandex Cloud

# Creating a new Kubernetes project in Yandex Cloud


This tutorial describes how to run a new Kubernetes project in Yandex Cloud. You will deploy an application from [Yandex Container Registry](../../container-registry/index.md) in a [Managed Service for Kubernetes cluster](../concepts/index.md#kubernetes-cluster) and publish it on the internet via the [Yandex Application Load Balancer](../../application-load-balancer/index.md) ingress controller.

If you need to create a Managed Service for Kubernetes cluster with no internet access, see [Creating and configuring a Kubernetes cluster with no internet access](k8s-cluster-with-no-internet.md).

To launch an app:

1. [Create service accounts](#create-sa).
1. [Create security groups](#create-sg).
1. [Set up Kubernetes resources](#create-k8s-res).
1. [Connect to the Managed Service for Kubernetes cluster](#cluster-connect).
1. [Set up Container Registry resources](#create-cr-res).
1. [Set up an Application Load Balancer](#setup-alb).
1. [Create a load balancer](#create-ingress).

If you no longer need the resources you created, [delete them](#clear-out).


## Required paid resources {#paid-resources}

The support cost for this solution includes:

* Fee for a DNS zone and DNS requests (see [Cloud DNS pricing](../../dns/pricing.md)).
* Fee for using the master and outgoing traffic in a Managed Service for Kubernetes cluster (see [Managed Service for Kubernetes pricing](../pricing.md)).
* Fee for using computing resources, OS, and storage in cluster nodes (VMs) (see [Compute Cloud pricing](../../compute/pricing.md)).
* Container Registry fee for using the storage and outgoing traffic (see [Container Registry pricing](../../container-registry/pricing.md)).
* Fee for using L7 load balancer's computing resources (see [Application Load Balancer pricing](../../application-load-balancer/pricing.md)).
* Fee for a public IP address for an L7 load balancer (see [Virtual Private Cloud pricing](../../vpc/pricing.md#prices-public-ip)).


## Getting started {#before-you-begin}

1. If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../cli/quickstart.md#install).

   The folder used by default is the one specified when [creating](../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

1. Install [jq](https://stedolan.github.io/jq/):

   ```bash
   sudo apt update && sudo apt install jq
   ```

### Create a network and a subnet {#create-network}

1. [Create a network](../../vpc/operations/network-create.md) named `yc-auto-network`:

   ```bash
   yc vpc network create --name yc-auto-network
   ```

1. [Create a subnet](../../vpc/operations/subnet-create.md) in the `ru-central1-a` [availability zone](../../overview/concepts/geo-scope.md):

   ```bash
   yc vpc subnet create \
     --name yc-auto-subnet-0 \
     --network-name yc-auto-network \
     --range 192.168.1.0/24 \
     --zone ru-central1-a
   ```

{% note warning %}

Do not change or delete Virtual Private Cloud resources used by the Managed Service for Kubernetes cluster. This may cause cluster errors or make it impossible to delete later.

{% endnote %}

### Register a domain zone and add a certificate {#register-domain}

1. [Register a public domain zone and delegate your domain](../../dns/operations/zone-create-public.md).

1. If you already have a [certificate](../../certificate-manager/concepts/index.md#types) for the [domain zone](../../dns/concepts/dns-zone.md), [add its details](../../certificate-manager/operations/import/cert-create.md) to [Yandex Certificate Manager](../../certificate-manager/index.md).

   If you have no certificate, issue a new Let's Encrypt® certificate and [add](../../certificate-manager/operations/managed/cert-create.md) it to Certificate Manager.
1. Get the certificate ID:

   ```bash
   yc certificate-manager certificate list
   ```

   Result:

   ```text
   +-----------------+-------+----------------+---------------------+----------+--------+
   |       ID        | NAME  |    DOMAINS     |      NOT AFTER      |   TYPE   | STATUS |
   +-----------------+-------+----------------+---------------------+----------+--------+
   | <ID> | <name> | <domain_name> | 2022-04-06 17:19:37 | IMPORTED | ISSUED |
   +-----------------+-------+----------------+---------------------+----------+--------+
   ```

## Create service accounts {#create-sa}

For a Managed Service for Kubernetes cluster and [load balancer](../../application-load-balancer/concepts/application-load-balancer.md), you need the following [service accounts](../../iam/concepts/users/service-accounts.md):
* Service account with the `k8s.clusters.agent` and `vpc.publicAdmin` [roles](../security/index.md#yc-api) for the folder where you want to create a Managed Service for Kubernetes cluster. This service account will be used to create the resources for the Managed Service for Kubernetes cluster.
* Service account with the [container-registry.images.puller](../../container-registry/security/index.md#container-registry-images-puller) role for the folder containing the [Docker image](../../container-registry/concepts/docker-image.md) [registry](../../container-registry/concepts/registry.md). The [nodes](../concepts/index.md#node-group) will use this service account to pull the required Docker images from the registry.
* For the Application Load Balancer ingress controller, you need service accounts with the following roles:
  * [alb.editor](../../application-load-balancer/security/index.md#alb-editor): To create the required resources.
  * [vpc.publicAdmin](../../vpc/security/index.md#vpc-public-admin): To manage [external connectivity](../../vpc/security/index.md#roles-list).
  * [certificate-manager.certificates.downloader](../../certificate-manager/security/index.md#certificate-manager-certificates-downloader): To use certificates registered in [Certificate Manager](../../certificate-manager/index.md).
  * [compute.viewer](../../compute/security/index.md#compute-viewer): To use Managed Service for Kubernetes cluster nodes in the load balancer [target groups](../../application-load-balancer/concepts/target-group.md).

### Service account for resources {#res-sa}

To create a service account that will be used to create the resources for theManaged Service for Kubernetes cluster:
1. Save the folder ID from your Yandex Cloud CLI profile configuration to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     FOLDER_ID=$(yc config get folder-id)
     ```

   - PowerShell {#powershell}

     ```shell script
     $FOLDER_ID = yc config get folder-id
     ```

   {% endlist %}

1. Create a service account:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     yc iam service-account create --name k8s-res-sa-$FOLDER_ID
     ```

   - PowerShell {#powershell}

     ```shell script
     yc iam service-account create --name k8s-res-sa-$FOLDER_ID
     ```

   {% endlist %}

1. Save the service account ID to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     RES_SA_ID=$(yc iam service-account get --name k8s-res-sa-$FOLDER_ID --format json | jq .id -r)
     ```

   - PowerShell {#powershell}

     ```shell script
     $RES_SA_ID = (yc iam service-account get --name k8s-res-sa-$FOLDER_ID --format json | ConvertFrom-Json).id
     ```

   {% endlist %}

1. Assign the [editor](../../iam/roles-reference.md#editor) role for the folder to the service account:

   ```bash
   yc resource-manager folder add-access-binding \
     --id $FOLDER_ID \
     --role editor \
     --subject serviceAccount:$RES_SA_ID
   ```

### Service account for nodes {#node-sa}

To create a service account the nodes will use to pull the required Docker images from the registry:
1. Save the folder ID from your Yandex Cloud CLI profile configuration to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     FOLDER_ID=$(yc config get folder-id)
     ```

   - PowerShell {#powershell}

     ```shell script
     $FOLDER_ID = yc config get folder-id
     ```

   {% endlist %}

1. Create a service account:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     yc iam service-account create --name k8s-node-sa-$FOLDER_ID
     ```

   - PowerShell {#powershell}

     ```shell script
     yc iam service-account create --name k8s-node-sa-$FOLDER_ID
     ```

   {% endlist %}

1. Save the service account ID to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     NODE_SA_ID=$(yc iam service-account get --name k8s-node-sa-$FOLDER_ID --format json | jq .id -r)
     ```

   - PowerShell {#powershell}

     ```shell script
     $NODE_SA_ID = (yc iam service-account get --name k8s-node-sa-$FOLDER_ID --format json | ConvertFrom-Json).id
     ```

   {% endlist %}

1. Assign the [container-registry.images.puller](../../container-registry/security/index.md#container-registry-images-puller) role for the folder to the service account:

   ```bash
   yc resource-manager folder add-access-binding \
     --id $FOLDER_ID \
     --role container-registry.images.puller \
     --subject serviceAccount:$NODE_SA_ID
   ```

### Service account for the Application Load Balancer ingress controller {#ic-sa}

1. Save the folder ID from your Yandex Cloud CLI profile configuration to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     FOLDER_ID=$(yc config get folder-id)
     ```

   - PowerShell {#powershell}

     ```shell script
     $FOLDER_ID = yc config get folder-id
     ```

   {% endlist %}

1. Create a service account:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     yc iam service-account create --name k8s-ic-sa-$FOLDER_ID
     ```

   - PowerShell {#powershell}

     ```shell script
     yc iam service-account create --name k8s-ic-sa-$FOLDER_ID
     ```

   {% endlist %}

1. Save the service account ID to a variable:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     IC_SA_ID=$(yc iam service-account get --name k8s-ic-sa-$FOLDER_ID --format json | jq .id -r)
     ```

   - PowerShell {#powershell}

     ```shell script
     $RES_SA_ID = (yc iam service-account get --name k8s-ic-sa-$FOLDER_ID --format json | ConvertFrom-Json).id
     ```

   {% endlist %}

1. Assign the following roles for the folder to the service account:
   * [alb.editor](../../application-load-balancer/security/index.md#alb-editor).
   * [vpc.publicAdmin](../../vpc/security/index.md#vpc-public-admin).
   * [certificate-manager.certificates.downloader](../../certificate-manager/security/index.md#certificate-manager-certificates-downloader).
   * [compute.viewer](../../compute/security/index.md#compute-viewer).

   ```bash
   yc resource-manager folder add-access-binding \
     --id $FOLDER_ID \
     --role alb.editor \
     --role vpc.publicAdmin \
     --role certificate-manager.certificates.downloader \
     --role compute.viewer \
     --subject serviceAccount:$IC_SA_ID
   ```

1. Create an [authorized key](../../iam/concepts/authorization/access-key.md) for the service account and save it to a file named `sa-key.json`:

   ```bash
   yc iam key create \
     --service-account-id $IC_SA_ID \
     --output sa-key.json
   ```

## Create security groups {#create-sg}

[Create security groups](../operations/connect/security-groups.md) for the Managed Service for Kubernetes cluster and its node groups.

To access Container Registry, [add a rule](../operations/connect/security-groups.md#rules-internal-nodegroup) for outgoing traffic that allows nodes to connect to external resources.

[Configure](../../application-load-balancer/tools/k8s-ingress-controller/security-groups.md) the security groups required for Application Load Balancer as well.

{% note warning %}

The configuration of security groups determines performance and availability of the cluster and the services and applications running in it.

{% endnote %}

## Set up Kubernetes resources {#create-k8s-res}

## Set up Kubernetes resources {#create-k8s-res}

### Create a Managed Service for Kubernetes cluster {#create-cluster}

{% note tip %}

This example uses the basic cluster parameters. Once the cluster is created, you cannot change some of its settings, e.g., [Container Network Interface](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) you selected, [secret encryption](../concepts/encryption.md#k8s-secrets-encryption) using Yandex Key Management Service, and a number of others. We recommend checking out [our detailed guide on creating a Managed Service for Kubernetes cluster](../operations/kubernetes-cluster/kubernetes-cluster-create.md#kubernetes-cluster-create).

{% endnote %}

Create a [Managed Service for Kubernetes cluster](../concepts/index.md#kubernetes-cluster) and specify the previously created [service accounts](../../iam/concepts/users/service-accounts.md) in the `--service-account-id` and `--node-service-account-id` parameters, and security groups in the `--security-group-ids` parameter.

{% list tabs group=programming_language %}

- Bash {#bash}

  Run this command:

  ```bash
  yc managed-kubernetes cluster create \
    --name k8s-demo \
    --network-name yc-auto-network \
    --zone ru-central1-a \
    --subnet-name yc-auto-subnet-0 \
    --public-ip \
    --service-account-id $RES_SA_ID \
    --node-service-account-id $NODE_SA_ID \
    --security-group-ids <security_group_IDs>
  ```

- PowerShell {#powershell}

  Run this command:

  ```shell script
  yc managed-kubernetes cluster create `
    --name k8s-demo `
    --network-name yc-auto-network `
    --zone ru-central1-a `
    --subnet-name yc-auto-subnet-0 `
    --public-ip `
    --service-account-id $RES_SA_ID `
    --node-service-account-id $NODE_SA_ID `
    --security-group-ids <security_group_IDs>
  ```

{% endlist %}

### Create a Managed Service for Kubernetes node group {#create-node-groups}

{% note warning %}

To access the Container Registry, a public IP address must be assigned to cluster nodes. Alternatively, a [NAT gateway](../../vpc/operations/create-nat-gateway.md) or [NAT instance](../../vpc/tutorials/nat-instance/index.md) must be configured in the node subnet.

For more information, see [Internet access for cluster worker nodes](../concepts/network.md#nodes-internet).

{% endnote %}

1. Make sure the Managed Service for Kubernetes cluster has been created successfully.
   1. In the [management console](https://console.yandex.cloud), select the [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) where you created the Managed Service for Kubernetes cluster.
   1. Navigate to **Managed Service for&nbsp;Kubernetes**.
   1. Make sure the Managed Service for Kubernetes cluster has been created successfully:
      * The **Status** column should state `Running`.
      * The **State** column should state `Healthy`.
1. Create a [Managed Service for Kubernetes node group](../concepts/index.md#node-group) and specify the previously created security groups in the `--network-interface security-group-ids` parameter:

   {% list tabs group=programming_language %}

   - Bash {#bash}

     ```bash
     yc managed-kubernetes node-group create \
       --name k8s-demo-ng \
       --cluster-name k8s-demo \
       --platform standard-v3 \
       --cores 2 \
       --memory 4 \
       --core-fraction 50 \
       --disk-type network-ssd \
       --fixed-size 2 \
       --network-interface subnets=yc-auto-subnet-0,ipv4-address=nat,security-group-ids=[<security_group_IDs>] \
       --async
     ```

   - PowerShell {#powershell}

     ```shell script
     yc managed-kubernetes node-group create `
       --name k8s-demo-ng `
       --cluster-name k8s-demo `
       --platform standard-v3 `
       --cores 2 `
       --memory 4 `
       --core-fraction 50 `
       --disk-type network-ssd `
       --fixed-size 2 `
       --network-interface subnets=yc-auto-subnet-0,ipv4-address=nat,security-group-ids=[<security_group_IDs>] `
       --async
     ```

   {% endlist %}

## Connect to the Managed Service for Kubernetes cluster {#cluster-connect}

[Install kubect](https://kubernetes.io/docs/tasks/tools/install-kubectl) and [configure it to work with the new cluster](../operations/connect/index.md#kubectl-connect).

## Set up Container Registry resources {#create-cr-res}

### Create a registry {#registry-create}

Create a container registry:

```bash
yc container registry create --name yc-auto-cr
```

### Configure a Docker credential helper {#config-ch}

To simplify authentication in Container Registry, configure a [Docker credential helper](../../container-registry/operations/authentication.md#cred-helper). It enables you to use private Yandex Cloud registries without running the `docker login` command.

To configure a credential helper, run this command:

```bash
yc container registry configure-docker
```

### Set up a Docker image {#docker-image}

Build a Docker image and push it to the registry.
1. Create a Dockerfile named `hello.dockerfile` and paste the following lines into it:

   ```docker
   FROM nginx
   CMD echo "Hi, I'm inside"
   ```

1. Build the Docker image.
   1. Get the ID of the [previously created](#registry-create) registry and save it to a variable:

      {% list tabs group=programming_language %}

      - Bash {#bash}

        ```bash
        REGISTRY_ID=$(yc container registry get --name yc-auto-cr --format json | jq .id -r)
        ```

      - PowerShell {#powershell}

        ```shell script
        $REGISTRY_ID = (yc container registry get --name yc-auto-cr --format json | ConvertFrom-Json).id
        ```

      {% endlist %}

   1. Build the Docker image:

      ```bash
      docker build . -f hello.dockerfile -t cr.yandex/$REGISTRY_ID/nginx:hello
      ```

   1. Push the Docker image to the registry:

      ```bash
      docker push cr.yandex/$REGISTRY_ID/nginx:hello
      ```

1. Make sure the image is now in the registry:

   ```bash
   yc container image list
   ```

   Result:

   ```text
   +----------------------+---------------------+----------------------------+-------+-----------------+
   |          ID          |       CREATED       |            NAME            | TAGS  | COMPRESSED SIZE |
   +----------------------+---------------------+----------------------------+-------+-----------------+
   | crpa2mf008mp******** | 2019-11-20 11:52:17 | crp71hkgiolp********/nginx | hello | 27.5 MB         |
   +----------------------+---------------------+----------------------------+-------+-----------------+
   ```

### Run the test app {#test-app}

Create a [pod](../concepts/index.md#pod) with the app from the Docker image and make sure no additional authentication in Container Registry was required to push the Docker image.
1. Run the pod with the app from the Docker image:

   ```bash
   kubectl run --attach hello-nginx --image cr.yandex/$REGISTRY_ID/nginx:hello
   ```

1. Make sure the pod status changed to `Running` state and get the full pod name.

   ```bash
   kubectl get pods
   ```

   Result:

   ```text
   NAME                          READY  STATUS   RESTARTS  AGE
   hello-nginx-5847fb96**-*****  1/1    Running  0         1h
   ```

1. Check the logs of the container running on that pod:

   ```bash
   kubectl logs hello-nginx-5847fb96**-*****
   ```

   Result:

   ```text
   Hi, I'm inside
   ```

   The pod pulled the Docker image with no additional authentication required on the Container Registry side.

## Set up an Application Load Balancer {#setup-alb}

To set up an [Application Load Balancer](https://yandex.cloud/en/marketplace/products/yc/alb-ingress-controller), follow [this guide](../operations/applications/alb-ingress-controller.md).

## Create a load balancer {#create-ingress}

1. Create a load balancer for [Kubernetes services](../concepts/index.md#service).
   1. Create the `ingress.yaml` file with the ingress controller manifest:

      ```yaml
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        name: alb-demo-tls
        annotations:
          ingress.alb.yc.io/subnets: <list_of_subnet_IDs>
          ingress.alb.yc.io/security-groups: <list_of_security_group_IDs>
          ingress.alb.yc.io/external-ipv4-address: <IP_address_assignment_method>
          ingress.alb.yc.io/group-name: <Ingress_resource_group_name>
      spec:
        tls:
          - hosts:
              - <domain_name>
            secretName: yc-certmgr-cert-id-<TLS_certificate_ID>
        rules:
          - host: <domain_name>
            http:
              paths:
                - pathType: Prefix
                  path: "/"
                  backend:
                    service:
                      name: service-hello
                      port:
                        name: http
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: service-hello
      spec:
        selector:
          run: hello-nginx
        type: NodePort
        ports:
          - name: http
            port: 80
            targetPort: 80
            protocol: TCP
            nodePort: 30080
      ```

      Where:
      * `ingress.alb.yc.io/subnets`: Specify one or more [subnets](../../vpc/concepts/network.md#subnet) for the Application Load Balancer.
      * `ingress.alb.yc.io/security-groups`: Specify one or more [security groups](../../application-load-balancer/concepts/application-load-balancer.md#security-groups) for the Application Load Balancer. If you skip this parameter, the default security group will be used. At least one of the security groups must allow an outgoing TCP connection to port 10501 in the node group subnet or to its security group.
      * `ingress.alb.yc.io/external-ipv4-address`: To get a new IP address and enable public access to an Application Load Balancer from the internet, specify the [previously obtained IP address](../../vpc/operations/get-static-ip.md) or set the value to `auto`.

        If you set `auto`, deleting the ingress controller will also remove its associated IP address from the cloud. To avoid this, use a reserved IP address.
      * `ingress.alb.yc.io/group-name`: Specify the group name. The group combines Kubernetes Ingress resources served by a single Application Load Balancer instance.
   1. Create a load balancer:

      ```bash
      kubectl apply -f ingress.yaml
      ```

   1. Wait until the load balancer is created and assigned a public IP address. This may take several minutes:

      ```bash
      kubectl get ingress alb-demo-tls
      ```

      The expected result is a non-empty value in the `ADDRESS` field for the new load balancer:

      ```bash
      NAME          CLASS   HOSTS           ADDRESS     PORTS    AGE
      alb-demo-tls  <none>  <domain_name>  <IP_address>  80, 443  15h
      ```

      The system will automatically deploy an [L7 load balancer](../../application-load-balancer/concepts/application-load-balancer.md) based on the load balancer configuration.
1. Follow the `https://<domain_name>` link to make sure your application is successfully published.

    {% note info %}
    
    If you cannot access the resource at the specified URL, [make sure](../operations/connect/security-groups.md) the security groups for the Managed Service for Kubernetes cluster and its node groups are configured correctly. If a rule is missing, [add it](../../vpc/operations/security-group-add-rule.md).
    
    {% endnote %}

## Delete the resources you created {#clear-out}

Some resources are not free of charge. Delete the resources you no longer need to avoid paying for them:

1. [Delete the Managed Service for Kubernetes cluster](../operations/kubernetes-cluster/kubernetes-cluster-delete.md):

   ```bash
   yc managed-kubernetes cluster delete --name k8s-demo
   ```

1. Delete the service accounts.
   * Delete the service account for resources:

     ```bash
     yc iam service-account delete --id $RES_SA_ID
     ```

   * Delete the service account for nodes:

     ```bash
     yc iam service-account delete --id $NODE_SA_ID
     ```

   * Delete the service account for the load balancer:

     ```bash
     yc iam service-account delete --id $IC_SA_ID
     ```

1. Delete the Container Registry resources.
   1. Get the ID of the Docker image pushed to the registry:

      {% list tabs group=programming_language %}

      - Bash {#bash}

        ```bash
        IMAGE_ID=$(yc container image list --format json | jq .[0].id -r)
        ```

      - PowerShell {#powershell}

        ```powershell
        $IMAGE_ID = (yc container image list --format json | ConvertFrom-Json).id
        ```

      {% endlist %}

   1. [Delete the Docker image](../../container-registry/operations/docker-image/docker-image-delete.md):

      ```bash
      yc container image delete --id $IMAGE_ID
      ```

   1. [Delete the registry](../../container-registry/operations/registry/registry-delete.md):

      ```bash
      yc container registry delete --name yc-auto-cr
      ```

1. Delete the [L7 load balancer](../../application-load-balancer/operations/application-load-balancer-delete.md).