学习示例:第12部分 - 与Pulumi一起部署GO应用程序
#初学者 #go #docker #pulumi

在以前的文章中,我们创建了一个HTTP REST API server和另一种应用程序,我们在本地手动部署了它们。

手动部署应用程序很酷,但是今天,我们将尝试使用pulumi来编程,当然是我们很棒的应用程序。

准备好了?

缅甸

Pulumi

Pulumi是一种基础架构作为代码(IASC)工具,可让您使用编程语言构建基础架构。它支持各种编程语言:Python,Node.js,GO,Java,.net ...

Terraform一样,Pulumi具有基于提供商/插件的架构。有官方提供商(AWS,GCP,Kubernetes,Docker ...),但也可以创建我们自己的提供商。

pulumi的工作原理?

具体地,用户定义了Pulumi程序中所需的状态,并创建了所需的资源。

为了提供,更新或删除基础架构,PULUMI具有直观的命令行接口(CLI)。如果您熟悉Docker撰写CLI和Terraform CLI,您也将采用Pulumi CLI

让我们安装Pulumi Cli。
在本指南中,我们将使用啤酒安装,但您可以以多种方式安装follow the installation guide

$ brew install pulumi/tap/pulumi

让我们检查CLI在本地正确安装:

$ pulumi version
v3.77.1

先决条件

如果您想复制本文,则必须在Pulumi上创建一个帐户并具有access token

在我的身边,我正在使用一个免费的pulumi帐户。

我们想要什么?

作为代码(IAC)工具的基础架构最初用于在云提供商中部署基础架构,但我们也可以处理(部署,更改和破坏)GO应用程序,这就是我们在本文中所做的。

我们将部署两个应用程序:

  • 我们可爱的Gophers API列出了现有的地鼠,显示信息,创建,更新和删除Gopher
  • 一个node.js hmi Gophers API Watcher显示我们可爱的地鼠(谁不爱ui?^^)

您可能知道,我喜欢在容器中运行应用程序,因此我们将在容器中运行应用程序。

先决条件 - 从GO应用程序创建Docker映像

我们将使用pulumi中的Docker provider部署和运行我们的应用程序,以便在需要使用应用程序的Docker图像之前。

让我们做!

uh ...来吧lie ...您想真正用Pulumi和Docker Images创建创建一篇文章的部署?

是的,使用docker init命令,我们可以生成必要的Docker文件并轻松地创建我们的图像,而无需头痛或“ Marabout Tips”。

我已经在视频中解释了它:

因此,感谢docker init命令我生成了一个Dockerfile,该命令(用于不同的平台和架构),标记并推了图像。

我们的Gophers API可在Docker Hub上使用(带有几个标签,具体取决于您的主机平台):https://hub.docker.com/r/scraly/gophers-api

和Gophers API观察者:https://hub.docker.com/r/scraly/gophers-api-watcher

初始化

首先,我们可以在GitHub中创建我们的存储库(为了共享和开源)。

为此,我登录GitHub website,单击“存储库”链接,单击“新”绿色按钮,然后创建了一个名为“pulumi-gophers”的新存储库。

现在,在您的本地计算机中,git clone这个新存储库您想要:

$ git clone https://github.com/scraly/pulumi-gophers.git
$ cd pulumi-gophers

初始化我们的项目(提示应询问pulumi访问令牌):

$ pulumi new go --force
Manage your Pulumi stacks by logging in.
Run `pulumi login --help` for alternative login options.
Enter your access token from https://app.pulumi.com/account/tokens
    or hit <ENTER> to log in using your browser                   : 


  Welcome to Pulumi!

  Pulumi helps you create, deploy, and manage infrastructure on any cloud using
  your favorite language. You can get started today with Pulumi at:

      https://www.pulumi.com/docs/get-started/

  Tip: Resources you create with Pulumi are given unique names (a randomly
  generated suffix) by default. To learn more about auto-naming or customizing resource
  names see https://www.pulumi.com/docs/intro/concepts/resources/#autonaming.


This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name: (pulumi-gophers) 
project description: (A minimal Go Pulumi program) 
Created project 'pulumi-gophers'

Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (gophers) 
Created stack 'gophers'

Installing dependencies...

go: downloading github.com/pulumi/pulumi/sdk/v3 v3.60.1
go: downloading golang.org/x/net v0.7.0
...
go: downloading github.com/kr/text v0.2.0
Finished installing dependencies

Your new project is ready to go! 

To perform an initial deployment, run `pulumi up`

命令创建一个gophers堆栈和项目的代码组织:

$ tree
.
├── go.mod
├── go.sum
├── main.go
├── Pulumi.yaml
└── README.md

创建我们的应用程序(Pulumi GO程序)

我们的应用程序将:

  • 检索Gophers API docker image
  • 检索Gophers API Watcher Docker Image
  • 创建一个docker网络(感谢我们的容器应该相互通信)
  • 创建一个gophers-api容器并运行
  • 创建一个gophers-api-watcher contunr并运行

为此,我们将使用Pulumi官方Docker提供商。

让我们安装Pulumi SDK和Pulumi Docker提供商,以便在我们的代码中使用它:

$ go get github.com/pulumi/pulumi-docker/sdk/v3@v3.6.1
$ go get github.com/pulumi/pulumi/sdk/v3@v3.44.2

好,现在我们可以创建一个main.go文件并将以下代码复制到其中。

GO代码被组织成软件包。因此,首先,我们初始化了称为main的软件包,以及我们需要在主文件中导入和使用的所有依赖关系/库:

package main

import (
    "fmt"

    "github.com/pulumi/pulumi-docker/sdk/v3/go/docker"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

随着添加的导入,您可以开始创建包含我们应用的智能的main()函数:

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {

当您执行pulumi up命令以部署我们的应用程序时,Pulumi将运行我们将在pulumi.Run(func(ctx *pulumi.Context) error {块代码中编写的所有代码。

让我们编写我们的程序。

首先,我们定义配置:


        //Configuration
        protocol := "http://"
        //tag := "latest" //for linux/arm64
        tag := "linux-amd64" //if you run this program in a linux/amd64 arch, on GitPod for example ;-)

        cfg := config.New(ctx, "")
        gophersAPIPort := cfg.RequireFloat64("gophersAPIPort")
        gophersAPIWatcherPort := cfg.RequireFloat64("gophersAPIWatcherPort")

然后,我们使用您在配置中定义的标签从Docker Hub中拉出Gophers API Docker映像:

        // Pull the Gophers API image
        gophersAPIImageName := "gophers-api"
        gophersAPIImage, err := docker.NewRemoteImage(ctx, fmt.Sprintf("%v-image", gophersAPIImageName), &docker.RemoteImageArgs{
            Name: pulumi.String("scraly/" + gophersAPIImageName + ":" + tag),
        })
        if err != nil {
            return err
        }
        ctx.Export("gophersAPIDockerImage", gophersAPIImage.Name)

然后,我们使用您在配置中定义的标签从Docker Hub中摘下Gophers API Watcher Docker映像:

        // Pull the Gophers API Watcher (frontend/UI) image
        gophersAPIWatcherImageName := "gophers-api-watcher"
        gophersAPIWatcherImage, err := docker.NewRemoteImage(ctx, fmt.Sprintf("%v-image", gophersAPIWatcherImageName), &docker.RemoteImageArgs{
            Name: pulumi.String("scraly/" + gophersAPIWatcherImageName + ":" + tag),
        })
        if err != nil {
            return err
        }
        ctx.Export("gophersAPIWatcherDockerImage", gophersAPIWatcherImage.Name)

我们的容器需要相互连接,因此我们需要创建一个Docker网络:

        // Create a Docker network
        network, err := docker.NewNetwork(ctx, "network", &docker.NetworkArgs{
            Name: pulumi.String(fmt.Sprintf("services-%v", ctx.Stack())),
        })
        if err != nil {
            return err
        }
        ctx.Export("containerNetwork", network.Name)

创建Gophers API容器:


        // Create the Gophers API container
        _, err = docker.NewContainer(ctx, "gophers-api", &docker.ContainerArgs{
            Name:  pulumi.String(fmt.Sprintf("gophers-api-%v", ctx.Stack())),
            Image: gophersAPIImage.RepoDigest,
            Ports: &docker.ContainerPortArray{
                &docker.ContainerPortArgs{
                    Internal: pulumi.Int(gophersAPIPort),
                    External: pulumi.Int(gophersAPIPort),
                },
            },
            NetworksAdvanced: &docker.ContainerNetworksAdvancedArray{
                &docker.ContainerNetworksAdvancedArgs{
                    Name: network.Name,
                    Aliases: pulumi.StringArray{
                        pulumi.String(fmt.Sprintf("gophers-api-%v", ctx.Stack())),
                    },
                },
            },
        })
        if err != nil {
            return err
        }

创建Gophers API观察器容器:

        // Create the Gophers API Watcher container
        _, err = docker.NewContainer(ctx, "gophers-api-watcher", &docker.ContainerArgs{
            Name:  pulumi.String(fmt.Sprintf("gophers-api-watcher-%v", ctx.Stack())),
            Image: gophersAPIWatcherImage.RepoDigest,
            Ports: &docker.ContainerPortArray{
                &docker.ContainerPortArgs{
                    Internal: pulumi.Int(gophersAPIWatcherPort),
                    External: pulumi.Int(gophersAPIWatcherPort),
                },
            },
            Envs: pulumi.StringArray{
                pulumi.String(fmt.Sprintf("PORT=%v", gophersAPIWatcherPort)),
                pulumi.String(fmt.Sprintf("HTTP_PROXY=backend-%v:%v", ctx.Stack(), gophersAPIPort)),
                pulumi.String(fmt.Sprintf("PROXY_PROTOCOL=%v", protocol)),
            },
            NetworksAdvanced: &docker.ContainerNetworksAdvancedArray{
                &docker.ContainerNetworksAdvancedArgs{
                    Name: network.Name,
                    Aliases: pulumi.StringArray{
                        pulumi.String(fmt.Sprintf("gophers-api-watcher-%v", ctx.Stack())),
                    },
                },
            },
        })
        if err != nil {
            return err
        }

        return nil
    })
}

就是这样!我们在基础架构中定义了我们想要的所有内容:借助Pulumi,在容器中运行的2个应用程序。

配置

如您所见,我们没有对应用程序端口号进行编码:

        cfg := config.New(ctx, "")
        gophersAPIPort := cfg.RequireFloat64("gophersAPIPort")
        gophersAPIWatcherPort := cfg.RequireFloat64("gophersAPIWatcherPort")

相反,我们将它们定义为配置参数。该程序将将它们放在Pulumi.<your-stack-name>.yaml文件中。

要定义它们并生成配置文件,请执行以下命令:

$ pulumi config set gophersAPIPort 8080
$ pulumi config set gophersAPIWatcherPort 8000

编辑我们的main.go文件并定义了我们的配置字段和值后,是时候要求下载并安装所有GO提供者和依赖项:

$ go mod tidy

让我们部署我们的应用程序

现在我们可以部署我们的应用程序,只需执行pulumi up comand。
这将显示所需状态的计划/预览。提示将要求您选择堆栈(默认情况下为dev),并确认您要执行/应用更改。

$ pulumi up
Please choose a stack, or create a new one: gophers
Previewing update (gophers)

View in Browser (Ctrl+O): https://app.pulumi.com/scraly/pulumi-gophers/gophers/previews/cb2a49a5-e17e-4e58-9525-a1931b214b23

     Type                         Name                       Plan       
 +   pulumi:pulumi:Stack          pulumi-gophers-gophers     create     
 +   ├─ docker:index:RemoteImage  gophers-api-watcher-image  create     
 +   ├─ docker:index:RemoteImage  gophers-api-image          create     
 +   ├─ docker:index:Network      network                    create     
 +   ├─ docker:index:Container    gophers-api-watcher        create     
 +   └─ docker:index:Container    gophers-api                create     


Outputs:
    containerNetwork            : "services-gophers"
    gophersAPIDockerImage       : "scraly/gophers-api:linux-amd64"
    gophersAPIWatcherDockerImage: "scraly/gophers-api-watcher:linux-amd64"

Resources:
    + 6 to create

Do you want to perform this update? yes
Updating (gophers)

View in Browser (Ctrl+O): https://app.pulumi.com/scraly/pulumi-gophers/gophers/updates/3

     Type                         Name                       Status              
 +   pulumi:pulumi:Stack          pulumi-gophers-gophers     created (9s)        
 +   ├─ docker:index:Network      network                    created (2s)        
 +   ├─ docker:index:RemoteImage  gophers-api-watcher-image  created (8s)        
 +   ├─ docker:index:RemoteImage  gophers-api-image          created (5s)        
 +   ├─ docker:index:Container    gophers-api                created (0.97s)     
 +   └─ docker:index:Container    gophers-api-watcher        created (1s)        


Outputs:
    containerNetwork            : "services-gophers"
    gophersAPIDockerImage       : "scraly/gophers-api:linux-amd64"
    gophersAPIWatcherDockerImage: "scraly/gophers-api-watcher:linux-amd64"

Resources:
    + 6 created

Duration: 13s

我们可以检查图像是否已成功从注册表中提取:

$ docker image ls
REPOSITORY                   TAG           IMAGE ID       CREATED        SIZE
scraly/gophers-api-watcher   linux-amd64   ee8c626fdeab   3 hours ago    288MB
scraly/gophers-api           linux-amd64   83e5cf52694c   3 hours ago    22.6MB
scraly/gophers-api-watcher   latest        4de2009ea463   23 hours ago   286MB
scraly/gophers-api           latest        0e32fa8f8e18   4 months ago   22.3MB

并检查容器是否也在运行:

 $ docker container ls
CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS         PORTS                    NAMES
ba0b23d0af11   scraly/gophers-api-watcher   "docker-entrypoint.s…"   9 minutes ago   Up 9 minutes   0.0.0.0:8000->8000/tcp   gophers-api-watcher-gophers
7b6d448f3d01   scraly/gophers-api           "/bin/server"            9 minutes ago   Up 9 minutes   0.0.0.0:8080->8080/tcp   gophers-api-gophers

让我们在本地测试

啊,我喜欢这一刻,我们将能够测试我们创建的内容!

首先,让我们测试我们的API。

$ curl localhost:8080/gophers
[{"displayname":"5th Element","name":"5th-element","url":"https://raw.githubusercontent.com/scraly/gophers/main/5th-element.png"}]

很酷,现在让我们展示我们的可爱HMI,因为它可以使用您喜欢的浏览器访问localhost:8000/demo

Gophers API Watcher

太棒了,它在工作(我们的Gopher真可爱^^)!

现在,如果您愿意,现在可以使用Gophers API(添加和编辑现有的地鼠)并观看它们出现在Horacio Gonzalezð。

的可爱HMI中。

清理

要轻松破坏创建的资源,您可以使用pulumi destroy命令。

$ pulumi destroy
Please choose a stack: gophers
Previewing destroy (gophers)

View in Browser (Ctrl+O): https://app.pulumi.com/scraly/pulumi-gophers/dev/previews/2344bad2-xxxx-xxxx-xxxx-846c828f7102

     Type                         Name                       Plan       
 -   pulumi:pulumi:Stack          pulumi-gophers-gophers     delete     
 -   ├─ docker:index:Container    gophers-api                delete     
 -   ├─ docker:index:Container    gophers-api-watcher        delete     
 -   ├─ docker:index:Network      network                    delete     
 -   ├─ docker:index:RemoteImage  gophers-api-image          delete     
 -   └─ docker:index:RemoteImage  gophers-api-watcher-image  delete     


Outputs:
  - containerNetwork            : "services-gophers"
  - gophersAPIDockerImage       : "scraly/gophers-api:linux-amd64"
  - gophersAPIWatcherDockerImage: "scraly/gophers-api-watcher:linux-amd64"

Resources:
    - 6 to delete

Do you want to perform this destroy? yes
Destroying (gophers)

View in Browser (Ctrl+O): https://app.pulumi.com/scraly/pulumi-gophers/gophers/updates/2

     Type                         Name                       Status              
 -   pulumi:pulumi:Stack          pulumi-gophers-gophers     deleted             
 -   ├─ docker:index:Container    gophers-api-watcher        deleted (0.26s)     
 -   ├─ docker:index:Container    gophers-api                deleted (0.51s)     
 -   ├─ docker:index:RemoteImage  gophers-api-watcher-image  deleted (0.64s)     
 -   ├─ docker:index:Network      network                    deleted (2s)        
 -   └─ docker:index:RemoteImage  gophers-api-image          deleted (0.86s)     


Outputs:
  - containerNetwork            : "services-gophers"
  - gophersAPIDockerImage       : "scraly/gophers-api:linux-amd64"
  - gophersAPIWatcherDockerImage: "scraly/gophers-api-watcher:linux-amd64"

Resources:
    - 6 deleted

Duration: 6s

The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained. 
If you want to remove the stack completely, run `pulumi stack rm gophers`.

已知的问题

创建失败

您第一次将与pulumi“玩”,特别是Docker provider,您可以面对我遇到的问题:

Do you want to perform this update? yes
Updating (gophers)

View in Browser (Ctrl+O): https://app.pulumi.com/scraly/pulumi-gophers/gophers/updates/1

     Type                         Name                       Status              
 +   pulumi:pulumi:Stack          pulumi-gophers-gophers     created (9s)        
 +   ├─ docker:index:Network      network                    created (2s)        
 +   ├─ docker:index:RemoteImage  gophers-api-watcher-image  created (8s)        
 +   ├─ docker:index:RemoteImage  gophers-api-image          created (5s)        
 +   ├─ docker:index:Container    gophers-api                ** creating failed**    1 error     

creating failed ...好吧...

我承认,当您遇到这个问题时,可能会令人失望。

找到正在发生的事情的方法是将--debug标志添加到pulumi up命令。

另一种方法是尝试在本地运行容器:

$ docker run scraly/gophers-api:latest 
WARNING: The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64/v3) and no specific platform was requested
exec /usr/local/bin/docker-entrypoint.sh: exec format error

您可以看到,我构建并将图像推入另一个平台/体系结构(Mac M1),而不是我在(ubuntu)上运行的服务器/机器。

fire gopher

因此,解决方案是在我的Mac,docker buildx build命令上使用platform标志,以使用好平台来构建和推动图像,例如:

$ docker buildx build --platform linux/amd64 -t scraly/gophers-api:linux-amd64 . --push

我在想什么

在结束这篇博客文章之前,我需要告诉您我对Pulumi的想法。

自2017年以来,我正在做很多Terraform,我培训了我的前同事,在许多项目中使用了它,用于几个云提供商(AWS,OVHCloud ...),甚至每天都在维护Terraform提供者。所以我承认我认为我不需要脉冲,因为我知道并使用了一个IAC工具。

您知道,我很好奇,所以我想,因为几年以来,我就没有时间进行测试。我想尝试以具体的需求,发现了一个:在OVHCloud上部署kubernetes群集和节点池(和其他资源)。

旅程并不容易,我遇到了一些麻烦(主要是tf2pulumi转换器,然后是原始的Pulumi OVH提供商)。我损失了几个小时,但没有放弃,感谢Engin,我们终于找到了解决方案。

我认为您可以拥有的Pulumi经验将取决于您将使用的提供商。

如果您已经知道Terraform,您将很容易理解Pulumi概念。如果您是开发人员,我认为用自己喜欢的语言编写基础架构可以更容易,而不是用HCL(Hashicorp配置语言)编写它。
但是,并非所有拥有Terraform提供商的公司都有Pulumi提供商,这是事实。

在我这边的旅程并不容易,但是我很固执,希望Engin Diri帮助了我。没有他的帮助,我可能会放弃或推迟几个月的时间。

结论

正如您在本文和以前的文章中所看到的那样,可以在GO中创建应用程序,甚至现在将它们部署在pulumi中。

我们应用程序的所有代码均可在以下方式提供:https://github.com/scraly/pulumi-gophers

因此,如果您愿意,现在可以使用Pulumi或其他工具来部署您的应用程序并自动化此任务。
对我来说,没有魔术棒,没有其他魔术工具和技术,而其他方法则取决于您的团队,上下文,需求,使用技术,工具或您想要的语言: - )。

在以下文章中,我们将在GO中创建其他应用程序/类型的应用程序。

希望您会喜欢的。