在以前的文章中,我们创建了一个HTTP REST API server和另一种应用程序,我们在本地手动部署了它们。
手动部署应用程序很酷,但是今天,我们将尝试使用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
:
太棒了,它在工作(我们的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)上运行的服务器/机器。
因此,解决方案是在我的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中创建其他应用程序/类型的应用程序。
希望您会喜欢的。