Как управлять инфраструктурой DigitalOcean и Kubernetes с помощью Pulumi

Автор выбралDiversity in Tech Fund для получения пожертвования в рамках программыWrite for DOnations.

Вступление

Pulumi - это инструмент для создания, развертывания и управления инфраструктурой с использованием кода, написанного на языках программирования общего назначения. Он поддерживает автоматизацию всех управляемых служб DigitalOcean, таких как капли, управляемые базы данных, записи DNS и кластеры Kubernetes, в дополнение к настройке приложения. Развертывания выполняются из простого в использовании интерфейса командной строки, который также интегрируется с широким спектром популярных систем CI / CD.

Pulumi поддерживает несколько языков, но в этом руководстве вы будете использоватьTypeScript, статически типизированную версиюJavaScript, которая использует среду выполненияNode.js. Это означает, что вы получите поддержку IDE и проверку во время компиляции, которая поможет убедиться, что вы настроили правильные ресурсы, использовали правильные ярлыки и т. Д., Сохраняя при этом возможность доступа к любым модулямNPM для служебных задач.

В этом руководстве вы подготовите кластер DigitalOceanKubernetes, приложение Kubernetes с балансировкой нагрузки и домен DNS DigitalOcean, который сделает ваше приложение доступным на стабильном доменном имени по вашему выбору. Все это можно реализовать в 60 строках кода инфраструктуры и в одном жесте командной строкиpulumi up. После этого урока вы будете готовы продуктивно создавать мощные облачные архитектуры, используя инфраструктуру как код Pulumi, которая использует всю площадь DigitalOcean и Kubernetes.

Предпосылки

Чтобы следовать этому уроку, вам понадобится:

  • Учетная запись DigitalOcean для развертывания ресурсов. Если у вас его еще нет,register here.

  • Токен API DigitalOcean для автоматического развертывания. Generate a personal access token here и держите его под рукой, так как вы будете использовать его на шаге 2.

  • Поскольку вы будете создавать и использовать кластер Kubernetes, вам потребуетсяinstall kubectl. Не беспокойтесь о дальнейшей настройке - вы сделаете это позже.

  • Вы напишите свою инфраструктуру как код в TypeScript, поэтому вам потребуется Node.js 8 или более поздней версии. Download it here или установитеusing your system’s package manager.

  • Вы будете использовать Pulumi для развертывания инфраструктуры, поэтому вам понадобитсяinstall the open source Pulumi SDK.

  • Для выполнения необязательного шага 5 вам потребуется доменное имя, настроенное для использования серверов имен DigitalOcean. This guide объясняет, как это сделать, для выбранного регистратора.

[[step-1 -—- scaffolding-a-new-project]] == Шаг 1. Создание строительных лесов для нового проекта

Первым шагом является создание каталога, в котором будет храниться ваш проект Pulumi. Этот каталог будет содержать исходный код для ваших определений инфраструктуры, в дополнение к файлам метаданных, описывающим проект и его зависимости NPM.

Сначала создайте каталог:

mkdir do-k8s

Затем перейдите во вновь созданный каталог:

cd do-k8s

С этого момента запускайте команды из вновь созданного каталогаdo-k8s.

Далее создайте новый проект Pulumi. Есть разные способы сделать это, но самый простой - использовать командуpulumi new с шаблоном проектаtypescript. Эта команда сначала предложит вам войти в Pulumi, чтобы сохранить ваш проект и состояние развертывания, а затем создаст простой проект TypeScript в текущем каталоге:

pulumi new typescript -y

Здесь вы передали параметр-y командеnew, которая сообщает ей, что нужно принять параметры проекта по умолчанию. Например, имя проекта берется из имени текущего каталога, поэтому будетdo-k8s. Если вы хотите использовать другие параметры для названия проекта, просто опустите-y.

После выполнения команды перечислите содержимое каталога с помощьюls:

ls

Следующие файлы теперь будут присутствовать:

OutputPulumi.yaml       index.ts          node_modules
package-lock.json package.json      tsconfig.json

Первичный файл, который вы будете редактировать, -index.ts. Хотя в этом руководстве используется только один этот файл, вы можете организовать проект так, как считаете нужным, с помощью модулей Node.js. В этом руководстве также описывается один шаг за раз, используя тот факт, что Pulumi может обнаруживать и постепенно развертывать только то, что изменилось. Если вы предпочитаете, вы можете просто заполнить всю программу и развернуть ее за один раз, используяpulumi up.

Теперь, когда вы создали свой новый проект, вы готовы добавить зависимости, необходимые для обучения.

[[step-2 -—- add-dependencies]] == Шаг 2 - Добавление зависимостей

Следующим шагом является установка и добавление зависимостей в пакеты DigitalOcean и Kubernetes. Сначала установите их с помощью NPM:

npm install @pulumi/digitalocean @pulumi/kubernetes

Это загрузит пакеты NPM, плагины Pulumi и сохранит их как зависимости.

Затем откройте файлindex.ts в своем любимом редакторе. Этот урок будет использовать нано:

nano index.ts

Замените содержимое вашегоindex.ts следующим:

index.ts

import * as digitalocean from "@pulumi/digitalocean";
import * as kubernetes from "@pulumi/kubernetes";

Это делает все содержимое этих пакетов доступным для вашей программы. Если вы наберете"digitalocean.", используя среду IDE, которая понимает TypeScript и Node.js, вы должны увидеть, например, список ресурсов DigitalOcean, поддерживаемых этим пакетом.

Сохраните и закройте файл после добавления содержимого.

[.note] #Note: Мы будем использовать подмножество того, что доступно в этих пакетах. Для получения полной документации по ресурсам, свойствам и связанным API обратитесь к соответствующей документации API для пакетов@pulumi/digitalocean и@pulumi/kubernetes.
#

Далее вы настроите свой токен DigitalOcean, чтобы Pulumi мог предоставлять ресурсы для вашей учетной записи:

pulumi config set digitalocean:token YOUR_TOKEN_HERE --secret

Обратите внимание на флаг--secret, который использует службу шифрования Pulumi для шифрования вашего токена, обеспечивая его хранение в зашифрованном виде. При желании вы можете вместо этого использовать переменную средыDIGITALOCEAN_TOKEN, но вам нужно будет помнить, что ее нужно устанавливать каждый раз при обновлении программы, тогда как при использовании конфигурации она автоматически сохраняется и используется для вашего проекта.

На этом шаге вы добавили необходимые зависимости и настроили свой токен API с помощью Pulumi, чтобы вы могли подготовить свой кластер Kubernetes.

[[step-3 -—- provisioning-a-kubernetes-cluster]] == Шаг 3. Подготовка кластера Kubernetes

Теперь вы готовы создать кластер DigitalOcean Kubernetes. Начните с повторного открытия файлаindex.ts:

nano index.ts

Добавьте эти строки в конец файлаindex.ts:

index.ts

...
const cluster = new digitalocean.KubernetesCluster("do-cluster", {
    region: digitalocean.Regions.SFO2,
    version: "latest",
    nodePool: {
        name: "default",
        size: digitalocean.DropletSlugs.DropletS2VPCU2GB,
        nodeCount: 3,
    },
});

export const kubeconfig = cluster.kubeConfigs[0].rawConfig;

Этот новый код выделяет экземплярdigitalocean.KubernetesCluster и устанавливает для него ряд свойств. Это включает в себя использованиеsfo2region slug, поддерживаемуюlatest версию Kubernetes,s-2vcpu-2gbDroplet size slug и указывает желаемое количество из трех экземпляров Droplet. Не стесняйтесь изменять любой из них, но имейте в виду, что DigitalOcean Kubernetes доступен только в определенных регионах на момент написания этой статьи. Вы можете обратиться кproduct documentation для получения обновленной информации о доступности региона.

Для получения полного списка свойств, которые вы можете настроить в своем кластере, обратитесь кKubernetesCluster API documentation.

Последняя строка в этом фрагменте кода экспортируетkubeconfig fileрезультирующего кластера Kubernetes, чтобы его было легко использовать. Экспортированные переменные выводятся на консоль, а также доступны для инструментов. Вы будете использовать это на мгновение для доступа к нашему кластеру с помощью стандартных инструментов, таких какkubectl.

Теперь вы готовы развернуть свой кластер. Для этого запуститеpulumi up:

pulumi up

Эта команда берет программу, генерирует план создания описанной инфраструктуры и выполняет серию шагов для развертывания этих изменений. Это работает для первоначального создания инфраструктуры в дополнение к возможности различать и обновлять вашу инфраструктуру при последующих обновлениях. В этом случае вывод будет выглядеть примерно так:

OutputPreviewing update (dev):

     Type                                     Name        Plan
 +   pulumi:pulumi:Stack                      do-k8s-dev  create
 +   └─ digitalocean:index:KubernetesCluster  do-cluster  create

Resources:
    + 2 to create

Do you want to perform this update?
  yes
> no
  details

Это говорит о том, что при обновлении будет создан один кластер Kubernetes с именемdo-cluster. Приглашениеyes/no/details позволяет нам подтвердить, что это желаемый результат, до того, как будут внесены какие-либо изменения. Если вы выберетеdetails, будет показан полный список ресурсов и их свойств. Выберитеyes, чтобы начать развертывание:

OutputUpdating (dev):

     Type                                     Name        Status
 +   pulumi:pulumi:Stack                      do-k8s-dev  created
 +   └─ digitalocean:index:KubernetesCluster  do-cluster  created

Outputs:
    kubeconfig: "..."

Resources:
    + 2 created

Duration: 6m5s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/1

Создание кластера займет несколько минут, но затем он будет запущен и полныйkubeconfig будет выведен на консоль. Сохранитеkubeconfig в файл:

pulumi stack output kubeconfig > kubeconfig.yml

А затем используйте его сkubectl для выполнения любой команды Kubernetes:

KUBECONFIG=./kubeconfig.yml kubectl get nodes

Вы получите вывод, подобный следующему:

OutputNAME           STATUS    ROLES     AGE       VERSION
default-o4sj   Ready         4m5s      v1.14.2
default-o4so   Ready         4m3s      v1.14.2
default-o4sx   Ready         3m37s     v1.14.2

На этом этапе вы настроили инфраструктуру как код и имеете повторяемый способ запуска и настройки новых кластеров DigitalOcean Kubernetes. На следующем шаге вы будете опираться на это, чтобы определить инфраструктуру Kubernetes в коде, и узнаете, как развертывать и управлять ими аналогичным образом.

[[step-4 -—- deploying-an-application-to-your-cluster]] == Шаг 4. Развертывание приложения в вашем кластере

Далее вы опишите конфигурацию приложения Kubernetes, используя инфраструктуру как код. Это будет состоять из трех частей:

  1. ОбъектProvider, который сообщает Pulumi о развертывании ресурсов Kubernetes в кластере DigitalOcean, а не по умолчанию для того, чтоkubectl настроено для использования.

  2. Kubernetes Deployment, который является стандартным способом Kubernetes для развертывания образа контейнера Docker, который реплицируется на любое количество подов.

  3. Kubernetes Service, который является стандартным способом указать Kubernetes доступ к балансировке нагрузки для целевого набора подов (в данном случае, развертывания выше).

Это довольно стандартныйreference architecture для начала работы с сервисом с балансировкой нагрузки в Kubernetes.

Чтобы развернуть все три из них, снова откройте файлindex.ts:

nano index.ts

Когда файл открыт, добавьте этот код в конец файла:

index.ts

...
const provider = new kubernetes.Provider("do-k8s", { kubeconfig })

const appLabels = { "app": "app-nginx" };
const app = new kubernetes.apps.v1.Deployment("do-app-dep", {
    spec: {
        selector: { matchLabels: appLabels },
        replicas: 5,
        template: {
            metadata: { labels: appLabels },
            spec: {
                containers: [{
                    name: "nginx",
                    image: "nginx",
                }],
            },
        },
    },
}, { provider });
const appService = new kubernetes.core.v1.Service("do-app-svc", {
    spec: {
        type: "LoadBalancer",
        selector: app.spec.template.metadata.labels,
        ports: [{ port: 80 }],
    },
}, { provider });

export const ingressIp = appService.status.loadBalancer.ingress[0].ip;

Этот код похож на стандартную конфигурацию Kubernetes, и поведение объектов и их свойств эквивалентны, за исключением того, что он написан на TypeScript вместе с другими вашими объявлениями инфраструктуры.

Сохраните и закройте файл после внесения изменений.

Как и раньше, запуститеpulumi up для предварительного просмотра, а затем разверните изменения:

pulumi up

После выбораyes для продолжения CLI распечатает подробные обновления статуса, включая диагностику доступности Pod, выделение IP-адресов и многое другое. Это поможет вам понять, почему развертывание может занять некоторое время или застревать.

Полный вывод будет выглядеть примерно так:

OutputUpdating (dev):

     Type                            Name        Status
     pulumi:pulumi:Stack             do-k8s-dev
 +   ├─ pulumi:providers:kubernetes  do-k8s      created
 +   ├─ kubernetes:apps:Deployment   do-app-dep  created
 +   └─ kubernetes:core:Service      do-app-svc  created

Outputs:
  + ingressIp : "157.230.199.202"

Resources:
    + 3 created
    2 unchanged

Duration: 2m52s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/2

После этого обратите внимание, что работает нужное количество модулей:

KUBECONFIG=./kubeconfig.yml kubectl get pods
OutputNAME                                   READY     STATUS    RESTARTS   AGE
do-app-dep-vyf8k78z-758486ff68-5z8hk   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-8982s   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-94k7b   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-cqm4c   1/1       Running   0          1m
do-app-dep-vyf8k78z-758486ff68-lx2d7   1/1       Running   0          1m

Подобно тому, как программа экспортирует файлkubeconfig кластера, эта программа также экспортирует результирующий IP-адрес балансировщика нагрузки службы Kubernetes. Используйте это дляcurl конечной точки и убедитесь, что она запущена и работает:

curl $(pulumi stack output ingressIp)
Output


Welcome to nginx!



Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

Отсюда вы можете легко редактировать и повторно развертывать инфраструктуру вашего приложения. Например, попробуйте изменить строкуreplicas: 5 наreplicas: 7, а затем повторно запуститьpulumi up:

pulumi up

Обратите внимание, что он просто показывает, что изменилось, а выбор деталей отображает точную разницу:

OutputPreviewing update (dev):

     Type                           Name        Plan       Info
     pulumi:pulumi:Stack            do-k8s-dev
 ~   └─ kubernetes:apps:Deployment  do-app-dep  update     [diff: ~spec]

Resources:
    ~ 1 to update
    4 unchanged

Do you want to perform this update? details
  pulumi:pulumi:Stack: (same)
    [urn=urn:pulumi:dev::do-k8s::pulumi:pulumi:Stack::do-k8s-dev]
    ~ kubernetes:apps/v1:Deployment: (update)
        [id=default/do-app-dep-vyf8k78z]
        [urn=urn:pulumi:dev::do-k8s::kubernetes:apps/v1:Deployment::do-app-dep]
        [provider=urn:pulumi:dev::do-k8s::pulumi:providers:kubernetes::do-k8s::80f36105-337f-451f-a191-5835823df9be]
      ~ spec: {
          ~ replicas: 5 => 7
        }

Теперь у вас есть как полнофункциональный кластер Kubernetes, так и работающее приложение. Когда ваше приложение запущено и работает, вы можете настроить собственный домен для использования с вашим приложением. Следующий шаг поможет вам настроить DNS с помощью Pulumi.

[[step-5 -—- Creating-a-dns-domain-optional]] == Шаг 5. Создание DNS-домена (необязательно)

Хотя кластер и приложение Kubernetes запущены и работают, адрес приложения зависит от прихоти автоматического назначения IP-адреса вашим кластером. При настройке и повторном развертывании этот адрес может измениться. На этом шаге вы увидите, как назначить настраиваемое DNS-имя IP-адресу балансировщика нагрузки, чтобы оно было стабильным даже при последующем изменении инфраструктуры.

[.note] #Note: Чтобы завершить этот шаг, убедитесь, что у вас есть домен, использующий DNS-серверы DNS DigitalOcean,ns1.digitalocean.com,ns2.digitalocean.com иns3.digitalocean.com. Инструкции по настройке доступны в разделе «Предварительные условия».
#

Чтобы настроить DNS, откройте файлindex.ts и добавьте следующий код в конец файла:

index.ts

...
const domain = new digitalocean.Domain("do-domain", {
    name: "your_domain",
    ipAddress: ingressIp,
});

Этот код создает новую запись DNS с записью A, которая ссылается на IP-адрес вашей службы Kubernetes. Заменитеyour_domain в этом фрагменте на выбранное вами доменное имя.

Часто требуется, чтобы дополнительные поддомены, напримерwww, указывали на веб-приложение. Это легко сделать с помощью DNS-записи DigitalOcean. Чтобы сделать этот пример более интересным, также добавьте записьCNAME, которая указываетwww.your_domain.com наyour_domain.com:

index.ts

...
const cnameRecord = new digitalocean.DnsRecord("do-domain-cname", {
    domain: domain.name,
    type: "CNAME",
    name: "www",
    value: "@",
});

Сохраните и закройте файл после внесения этих изменений.

Наконец, запуститеpulumi up, чтобы развернуть изменения DNS, чтобы они указывали на ваше существующее приложение и кластер:

OutputUpdating (dev):

     Type                             Name             Status
     pulumi:pulumi:Stack              do-k8s-dev
 +   ├─ digitalocean:index:Domain     do-domain        created
 +   └─ digitalocean:index:DnsRecord  do-domain-cname  created

Resources:
    + 2 created
    5 unchanged

Duration: 6s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/3

После распространения изменений DNS вы сможете получить доступ к своему контенту в своем пользовательском домене:

curl www.your_domain.com

Вы получите вывод, подобный следующему:

Output


Welcome to nginx!



Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

Таким образом, вы успешно настроили новый кластер DigitalOcean Kubernetes, развернули в нем приложение Kubernetes с балансировкой нагрузки и предоставили балансировщику нагрузки этого приложения стабильное доменное имя с использованием DigitalOcean DNS, всего в 60 строках кода иpulumi up команда.

Следующий шаг поможет вам удалить ресурсы, если они вам больше не нужны.

[[step-6 -—- remove-the-resources-optional]] == Шаг 6 - Удаление ресурсов (необязательно)

Перед завершением урока вы можете уничтожить все ресурсы, созданные выше. Это гарантирует, что вы не будете платить за ресурсы, которые не используются. Если вы предпочитаете поддерживать приложение в рабочем состоянии, не стесняйтесь пропустить этот шаг.

Выполните следующую команду, чтобы уничтожить ресурсы. Будьте осторожны при использовании этого, поскольку это не может быть отменено!

pulumi destroy

Как и в случае с командойup,destroy отображает предварительный просмотр и подсказку перед выполнением действий:

OutputPreviewing destroy (dev):

     Type                                     Name             Plan
 -   pulumi:pulumi:Stack                      do-k8s-dev       delete
 -   ├─ digitalocean:index:DnsRecord          do-domain-cname  delete
 -   ├─ digitalocean:index:Domain             do-domain        delete
 -   ├─ kubernetes:core:Service               do-app-svc       delete
 -   ├─ kubernetes:apps:Deployment            do-app-dep       delete
 -   ├─ pulumi:providers:kubernetes           do-k8s           delete
 -   └─ digitalocean:index:KubernetesCluster  do-cluster       delete

Resources:
    - 7 to delete

Do you want to perform this destroy?
  yes
> no
  details

Предполагая, что это то, что вы хотите, выберитеyes и посмотрите, как происходит удаление:

OutputDestroying (dev):

     Type                                     Name             Status
 -   pulumi:pulumi:Stack                      do-k8s-dev       deleted
 -   ├─ digitalocean:index:DnsRecord          do-domain-cname  deleted
 -   ├─ digitalocean:index:Domain             do-domain        deleted
 -   ├─ kubernetes:core:Service               do-app-svc       deleted
 -   ├─ kubernetes:apps:Deployment            do-app-dep       deleted
 -   ├─ pulumi:providers:kubernetes           do-k8s           deleted
 -   └─ digitalocean:index:KubernetesCluster  do-cluster       deleted

Resources:
    - 7 deleted

Duration: 7s

Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/4

На этом этапе ничего не остается: DNS-записи исчезли, а кластер Kubernetes - вместе с приложением, запущенным внутри него, - исчез. Постоянная ссылка по-прежнему доступна, поэтому вы можете вернуться и просмотреть полную историю обновлений для этого стека. Это может помочь вам восстановиться, если уничтожение было ошибкой, поскольку служба хранит полную историю состояний для всех ресурсов.

Если вы хотите полностью уничтожить ваш проект, удалите стек:

pulumi stack rm

Вы получите вывод с просьбой подтвердить удаление, введя имя стека:

OutputThis will permanently remove the 'dev' stack!
Please confirm that this is what you'd like to do by typing ("dev"):

В отличие от команды destroy, которая удаляет ресурсы облачной инфраструктуры, удаление стека полностью стирает всю историю вашего стека из сферы действия Pulumi.

Заключение

В этом руководстве вы развернули ресурсы инфраструктуры DigitalOcean - кластер Kubernetes и домен DNS с записями A и CNAME - в дополнение к конфигурации приложения Kubernetes, которая использует этот кластер. Вы сделали это, используя инфраструктуру как код, написанную на знакомом языке программирования TypeScript, который работает с существующими редакторами, инструментами и библиотеками и использует существующие сообщества и пакеты. Вы сделали все это, используя единый рабочий процесс командной строки для развертывания, которое охватывает ваше приложение и инфраструктуру.

Отсюда вы можете предпринять следующие шаги:

Весь образец из этого руководства -available on GitHub. Подробные сведения о том, как использовать инфраструктуру Pulumi как код в ваших собственных проектах сегодня, можно найти в руководствахPulumi Documentation,Tutorials илиGetting Started. Pulumi с открытым исходным кодом и бесплатное использование.

Related