作者选择了 Girls Who 代码作为 写给捐款计划的一部分,以获得捐款。
介绍
Docker是一个(https://en.wikipedia.org/wiki/OS-level_virtualisation)工具,用于为应用程序提供一个文件系统,其中包含运行所需的一切,确保软件具有一致的运行时间环境,并且无论部署在何处,都会以相同的方式行为。
通过利用Docker,您可以将应用程序部署到任何支持Docker的系统上,相信它将始终按预期运行。Kubernetes,与此同时,允许您在集群中部署多个节点的应用程序。
在本教程中,您将构建一个以 Go 编写的示例应用程序,并在开发机上本地运行,然后您将用 Docker 容器化应用程序,部署到 Kubernetes 集群中,并创建一个负载平衡器,将作为您的应用程序的公开入口点。
前提条件
在开始本教程之前,您将需要以下内容:
- 开发服务器或本地机器,您将从中部署应用程序。 虽然本指南中的指令将基本适用于大多数操作系统,但本教程假设您可以访问一个配置为非根用户有sudo权限的Ubuntu 18.04系统,这在我们[Ubuntu 18.04的初始服务器设置 (https://andsky.com/tech/tutorials/initial-server-setup-with-ubuntu-18-04)教程中有所描述.
- **联合国 安装在您的开发机器上的 " Docker " 命令行工具。 要安装此选项, 请遵循我们关于) 的教程中的 ** 第 1 和 2 节。
- 联合国 安装在您的开发机上的 " kubectl " 命令行工具。 要安装此选项, 请遵循 [ Kubernetes 官方文档中的此指南] (https://kubernetes.io/docs/tasks/tools/install-kubectl/# install-kubectl-on-linux ) 。
- Docker Hub上的免费账户,您将将其推向 Docker 图像。 要设置此功能,请访问Docker Hub网站,点击页面右上角的 Get Started** 按钮,并遵守注册说明.
- Kubernetes集群。 您可以按照我们的Kubernetes 快速启动指南提供[数字海洋库伯内特群 (https://www.digitalocean.com/products/kubernetes/)。 如果您从另一个云端提供者提供您的集群, 您仍然可以完成此教程 。 无论在哪里获取集群, 请确定设置一个配置文件, 并确保您可以从您的开发服务器连接到集群 。 .
步骤 1 — 在 Go 中构建样本 Web 应用程序
在此步骤中,您将构建一个以 Go 编写的样本应用程序. 一旦您将此应用程序与 Docker 集装,它将响应到您的服务器的 IP 地址的请求,以便在端口 `3000。
请通过更新服务器的包列表来开始,如果您最近没有这样做:
1sudo apt update
然后安装 Go 运行:
1sudo apt install golang
接下来,请确保您在您的主目录中,并创建一个新目录,该目录将包含您的所有项目文件:
1cd && mkdir go-app
然后导航到这个新目录:
1cd go-app/
使用nano
或您喜爱的文本编辑器创建一个名为main.go
的文件,该文件将包含您的Go应用程序的代码:
1nano main.go
任何 Go 源文件的第一个行总是是一个包
声明,它定义了该文件所属的代码包。
1[label go-app/main.go]
2package main
接下来,添加一个导入
声明,你可以列出应用程序需要的所有库,这里包括处理格式化文本输入和输出的fmt
和net/http
,提供HTTP客户端和服务器实现:
1[label go-app/main.go]
2package main
3
4import (
5 "fmt"
6 "net/http"
7)
接下来,定义一个‘homePage’函数,该函数将包含两个参数:‘http.ResponseWriter’和一个指向‘http.Request’的指针。在Go中,一个‘ResponseWriter’接口被用来构建一个HTTP响应,而‘http.Request’是一个代表一个接入请求的对象。
1[label go-app/main.go]
2. . .
3
4import (
5 "fmt"
6 "net/http"
7)
8
9func homePage(w http.ResponseWriter, r *http.Request) {
10 fmt.Fprintf(w, "My Awesome Go App")
11}
在此之后,添加一个setupRoutes
函数,该函数会将收到的请求映射到其预期的HTTP处理函数中。在这个setupRoutes
函数的体内,将/
路线的映射添加到新定义的homePage
函数中。
1[label go-app/main.go]
2. . .
3
4func homePage(w http.ResponseWriter, r *http.Request) {
5 fmt.Fprintf(w, "My Awesome Go App")
6}
7
8func setupRoutes() {
9 http.HandleFunc("/", homePage)
10}
最后,添加以下主
函数,这样就可以打印出一个表明您的应用已经启动的字符串,然后在您在端口3000
上聆听和服务您的 Go 应用程序之前,它会调用setupRoutes
函数。
1[label go-app/main.go]
2. . .
3
4func setupRoutes() {
5 http.HandleFunc("/", homePage)
6}
7
8func main() {
9 fmt.Println("Go Web App Started on Port 3000")
10 setupRoutes()
11 http.ListenAndServe(":3000", nil)
12}
添加这些行后,这就是最终文件的样子:
1[label go-app/main.go]
2package main
3
4import (
5 "fmt"
6 "net/http"
7)
8
9func homePage(w http.ResponseWriter, r *http.Request) {
10 fmt.Fprintf(w, "My Awesome Go App")
11}
12
13func setupRoutes() {
14 http.HandleFunc("/", homePage)
15}
16
17func main() {
18 fmt.Println("Go Web App Started on Port 3000")
19 setupRoutes()
20 http.ListenAndServe(":3000", nil)
21}
如果您使用nano
创建了此文件,请按CTRL + X
,Y
,然后按ENTER
。
接下来,使用以下go run
命令运行应用程序,将编译您的main.go
文件中的代码,并在开发机器上本地运行:
1go run main.go
1[secondary_label Output]
2Go Web App Started on Port 3000
此输出确认应用程序按预期运行,但将无限期运行,因此请按CTRL + C
来关闭。
在本指南中,您将使用此样本应用程序来实验Docker和Kubernetes。 为此,继续阅读以了解如何将您的应用程序集装到Docker中。
步骤 2 — Dockerizing 您的 Go 应用程序
在当前状态下,您刚刚创建的 Go 应用程序仅在您的开发服务器上运行。在此步骤中,您将通过将其集装在 Docker 中,使该新应用程序可移动。这将允许它在支持 Docker 容器的任何机器上运行。您将构建一个 Docker 图像并将其推到 Docker Hub 上的中央公共存储库。这样,您的 Kubernetes 集群可以将图像拖回并在集群中作为容器部署。
对您的应用程序进行集装的第一步是创建一个名为 [Dockerfile] 的特殊脚本(https://docs.docker.com/search/?q=dockerfile)。
<$>[注] 注: 在此步骤中,您将配置一个简单的 Docker 容器,以便在一个阶段内构建和运行您的 Go 应用程序. 如果在未来,您想要减少您的 Go 应用程序将在生产中运行的容器的大小,您可能需要查看 mutli-stage builds. <$>
创建一个名为Dockerfile
的新文件:
1nano Dockerfile
在文件的顶部,指定 Go 应用程序所需的基本图像:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
然后在容器中创建一个应用程序
目录,该目录将包含应用程序的源文件:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
3RUN mkdir /app
下面,添加以下行,将根
目录中的所有内容复制到应用
目录中:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
3RUN mkdir /app
4ADD . /app
接下来,添加以下行,将工作目录更改为app
,这意味着此 Dockerfile中的所有下列命令将从该位置运行:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
3RUN mkdir /app
4ADD . /app
5WORKDIR /app
添加一行指示Docker运行go build -o main
命令,该命令编译了Go应用程序的二进制可执行:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
3RUN mkdir /app
4ADD . /app
5WORKDIR /app
6RUN go build -o main .
然后添加最后一行,该行将运行二进制可执行:
1[label go-app/Dockerfile]
2FROM golang:1.12.0-alpine3.9
3RUN mkdir /app
4ADD . /app
5WORKDIR /app
6RUN go build -o main .
7CMD ["/app/main"]
添加这些行后保存和关闭文件。
现在你有这个Dockerfile
在你的项目的根,你可以创建一个Docker图像基于它使用以下docker构建
命令。
<$>[注] 注 :在Docker中,标签允许您传输特定图像的信息,例如其版本号码。下列命令不提供特定标签,因此Docker将以默认标签标签图像:‘最后’。
1docker build -t sammy/image_name:tag_name .
例如,您可以将标记为v1.1
的图像部署到制作中,但可以将另一个标记为v1.2
的图像部署到预制作或测试环境中。
您将传递的最后一个论点是路径: .
. 这说明您希望从当前工作目录的内容中构建 Docker 图像。
1docker build -t sammy/go-web-app .
此构建命令会读取Dockerfile
中的所有行,按顺序执行它们,然后缓存它们,允许未来的构建运行更快:
1[secondary_label Output]
2. . .
3Successfully built 521679ff78e5
4Successfully tagged go-web-app:latest
一旦此命令完成构建,您将能够在运行docker 图像
命令时看到您的图像:
1docker images
1[secondary_label Output]
2REPOSITORY TAG IMAGE ID CREATED SIZE
3sammy/go-web-app latest 4ee6cf7a8ab4 3 seconds ago 355MB
接下来,使用以下命令创建并启动基于您刚刚创建的图像的集装箱。该命令包括它
旗,该命令规定该集装箱将在交互式模式下运行。
1docker run -it -p 3000:3000 sammy/go-web-app
1[secondary_label Output]
2Go Web App Started on Port 3000
如果没有其他东西在该端口上运行,您将能够通过打开浏览器并导航到以下URL来查看应用程序的运作:
1http://your_server_ip:3000
<$>[注] 注: 如果您正在从本地计算机(而不是服务器)跟踪本教程,请转到下面的URL访问该应用程序:
1http://localhost:3000
美元
在检查应用程序在您的浏览器中按照预期工作后,通过在您的终端中按CTRL + C
来停止它。
当您将集装箱应用程序部署到您的 Kubernetes 集群时,您需要能够从集中位置提取图像,从而将新创建的图像推到 Docker Hub 图像存储库。
运行以下命令,从终端登录到 Docker Hub:
1docker login
这将提示您的 Docker Hub 用户名和密码. 正确输入后,您将在命令输出中看到成功登录
。
登录后,使用docker push
命令将新图像推到 Docker Hub,如下:
1docker push sammy/go-web-app
一旦此命令成功完成,您将能够打开您的 Docker Hub 帐户并在那里查看您的 Docker 图像。
现在你已经将你的图像推到一个中心位置,你已经准备好将其部署到你的Kubernetes集群中。
步骤 3 — 改进kubectl
的可用性
到目前为止,你已经创建了一个运行的Go应用程序,并将其集装在Docker中。然而,该应用程序仍然无法公开访问。 为了解决这个问题,你将使用kubectl
命令行工具部署你的新的Docker图像到你的Kubernetes集群中。 然而,在这样做之前,让我们对Kubernetes配置文件做出一个小小的更改,这将有助于使运行kubectl
命令变得更轻松。
默认情况下,当您使用kubectl
命令行工具运行命令时,您必须使用-kubeconfig
旗号指定群集配置文件的路径,但是,如果您的配置文件名为config
并存储在名为~/.kube
的目录中,则kubectl
将知道在哪里寻找配置文件,并且可以在没有-kubeconfig
旗号指向它的情况下获取它。
为此,如果您尚未这样做,请创建一个名为~/.kube
的新目录:
1mkdir ~/.kube
然后将集群配置文件移动到此目录,并在过程中更名为config
:
1mv clusterconfig.yaml ~/.kube/config
继续前进,在运行kubectl
时,您不需要指定集群配置文件的位置,因为该命令将能够找到它现在它处于默认位置。
1kubectl get nodes
在Kubernetes的背景下,一个节点是可以部署一个或多个节点的服务器或工作机器:
1[secondary_label Output]
2NAME STATUS ROLES AGE VERSION
3k8s-1-13-5-do-0-nyc1-1554148094743-1-7lfd Ready <none> 1m v1.13.5
4k8s-1-13-5-do-0-nyc1-1554148094743-1-7lfi Ready <none> 1m v1.13.5
5k8s-1-13-5-do-0-nyc1-1554148094743-1-7lfv Ready <none> 1m v1.13.5
通过创建两个Kubernetes对象,您将准备好将应用程序部署到您的Kubernetes集群中,另一个将创建负载平衡器,为您的应用程序提供一个访问点。
步骤4 - 创建部署
RESTful resources构成一个Kubernetes系统的所有持久实体,在这种情况下,它们通常被称为 Kubernetes objects。
一种叫"部署"的库伯内特斯天体(Kubernetes object)是一组完全相同,无法分辨的被子. 在Kubernetes中, a [pod(https://kubernetes.io/docs/concepts/workloads/pods/pod/)是由一个或多个容器组成的分组,它们能够通过同一个共享网络进行通信,并与同一个共享存储进行交互. 部署一次运行一个以上的母应用程序复制件,并自动替换任何失败的实例,确保您的应用程序总是可以服务用户请求.
在此步骤中,您将创建一个 Kubernetes 对象描述文件,也称为 manifest,用于部署。
首先,在项目的根目录中创建一个部署宣言: go-app/
. 对于像这个这样的小项目,将其保存在根目录中会最大限度地降低复杂性。
创建一个名为deployment.yml
的新文件:
1nano deployment.yml
Kubernetes API的不同版本包含不同的对象定义,所以在这个文件的顶部,你必须定义你正在使用的apiVersion
来创建这个对象。 为本教程的目的,你将使用apps/v1
组合,因为它包含许多你需要创建部署的核心Kubernetes对象定义。
1[label go-app/deployment.yml]
2---
3apiVersion: apps/v1
4kind: Deployment
然后为您的部署定义元数据
。每个Kubernetes对象都需要一个元数据
字段,因为它包含对象的独特名称
等信息。
1[label go-app/deployment.yml]
2---
3apiVersion: apps/v1
4kind: Deployment
5metadata:
6 name: go-web-app
接下来,你将构建你的deployment.yml
的spec
块。一个spec
字段是每个Kubernetes对象的要求,但它的确切格式不同于每个对象类型。在部署的情况下,它可以包含你想要运行的 replicas 的数量等信息。
1[label go-app/deployment.yml]
2. . .
3metadata:
4 name: go-web-app
5spec:
6 replicas: 5
接下来,在spec
块下创建一个选择器
块,这将作为你的 pods 的 _label selector。
在这个选择器
块中,定义匹配标签
并添加名称
标签。 基本上,匹配标签
字段告诉Kubernetes部署适用于哪些 pods。
1[label go-app/deployment.yml]
2. . .
3spec:
4 replicas: 5
5 selector:
6 matchLabels:
7 name: go-web-app
在此之后,再增加一个 " 临时 " 区块。 每一次部署都使用 " template " 块中指定的标签创建出一组吊舱。 这个区块的第一个分地是元数据',其中载有将适用于部署中所有舱口的
标签'。 这些标签是用作Kubernetes对象的识别属性的密钥/值对. 当您稍后定义服务时, 您可以指定您想要将带有此 name
标签的所有 pocket 归到该服务下 。 将这个 名称
标签设定为go- web- app
:
1[label go-app/deployment.yml]
2. . .
3spec:
4 replicas: 5
5 selector:
6 matchLabels:
7 name: go-web-app
8 template:
9 metadata:
10 labels:
11 name: go-web-app
这个模板
块的第二部分是spec
块,这与您之前添加的spec
块不同,因为它只适用于由模板
块创建的 pods,而不是整个部署。
在这个spec
块中,添加一个containers
字段,并再次定义一个名称
属性。这个名称
字段定义了由这个特定部署创建的任何容器的名称。
1[label go-app/deployment.yml]
2. . .
3 template:
4 metadata:
5 labels:
6 name: go-web-app
7 spec:
8 containers:
9 - name: application
10 image: sammy/go-web-app
接下来,在IfNotPresent
中添加一个imagePullPolicy
字段,该字段将指示部署只将图像拉上,如果它之前尚未这样做,然后,最后,添加一个port
块。
1[label go-app/deployment.yml]
2. . .
3 spec:
4 containers:
5 - name: application
6 image: sammy/go-web-app
7 imagePullPolicy: IfNotPresent
8 ports:
9 - containerPort: 3000
您的deployment.yml
的完整版本将看起来像这样:
1[label go-app/deployment.yml]
2---
3apiVersion: apps/v1
4kind: Deployment
5metadata:
6 name: go-web-app
7spec:
8 replicas: 5
9 selector:
10 matchLabels:
11 name: go-web-app
12 template:
13 metadata:
14 labels:
15 name: go-web-app
16 spec:
17 containers:
18 - name: application
19 image: sammy/go-web-app
20 imagePullPolicy: IfNotPresent
21 ports:
22 - containerPort: 3000
保存并关闭文件。
接下来,使用以下命令应用您的新部署:
1kubectl apply -f deployment.yml
<$>[注] 注: 有关所有可用于部署的配置的更多信息,请在这里查看官方的 Kubernetes 文档: Kubernetes 部署 <$>
在下一步中,您将创建另一种类型的 Kubernetes 对象,该对象将管理您如何访问新部署中存在的pods。该服务将创建负载平衡器,然后暴露单个 IP 地址,并将向该 IP 地址的请求分发给部署中的复制品。
步骤5:创建服务
现在你有一个成功的 Kubernetes 部署,你已经准备好将你的应用程序暴露在外部世界. 要做到这一点,你需要定义另一种类型的 Kubernetes 对象:一个 service。
<$>[注] 注意: 为了澄清,我们将将此服务对象定义为一个单独的文件。 但是,只要它们分为 ---
,可以在同一个 YAML 文件中组合多个资源宣言,请参阅 Kubernetes 文档中的此页面以获取更多细节。
创建一个名为service.yml
的新文件:
1nano service.yml
重新定义apiVersion
和kind
字段,以类似于您的deployment.yml
文件的方式启动此文件,这一次,将apiVersion
字段指向v1
,Kubernetes API通常用于服务:
1[label go-app/service.yml]
2---
3apiVersion: v1
4kind: Service
接下来,在一个元数据
块中添加你的服务名称,就像你在deployment.yml
中一样。
1[label go-app/service.yml]
2---
3apiVersion: v1
4kind: Service
5metadata:
6 name: go-web-service
接下来,创建一个spec
块,这个spec
块将与部署中包含的区块不同,它将包含该服务的类型
,以及端口转发配置和选择器
。
添加一个定义该服务的类型
的字段,并将其设置为LoadBalancer
。
<$>[警告] 警告: 本步描述的创建负载平衡器的方法只适用于来自支持外部负载平衡器的云提供商提供的Kubernetes集群。此外,请注意,从云提供商提供负载平衡器会产生额外的成本。
1[label go-app/service.yml]
2---
3apiVersion: v1
4kind: Service
5metadata:
6 name: go-web-service
7spec:
8 type: LoadBalancer
然后添加一个端口
块,定义您想要如何访问您的应用程序。
- 「名稱」,指向「http」 * 「端口」,指向「80」 * 「targetPort」,指向「3000」
这将把输入的HTTP请求放在端口80
上,并将其转发到3000
的targetPort
。
1[label go-app/service.yml]
2---
3apiVersion: v1
4kind: Service
5metadata:
6 name: go-web-service
7spec:
8 type: LoadBalancer
9 ports:
10 - name: http
11 port: 80
12 targetPort: 3000
最后,添加一个选择器
块,就像你在deployments.yml
文件中一样。这个选择器
块很重要,因为它将名为Go-web-app
的任何部署 pods 对该服务进行映射:
1[label go-app/service.yml]
2---
3apiVersion: v1
4kind: Service
5metadata:
6 name: go-web-service
7spec:
8 type: LoadBalancer
9 ports:
10 - name: http
11 port: 80
12 targetPort: 3000
13 selector:
14 name: go-web-app
添加这些行后,保存并关闭文件,然后再使用kubectl apply
命令将此服务应用到您的Kubernetes群集:
1kubectl apply -f service.yml
此命令将应用新的Kubernetes服务,并创建负载平衡器,该负载平衡器将作为您在集群内运行的应用程序的公开入口点。
要查看应用程序,您将需要新的负载平衡器的IP地址。
1kubectl get services
1[secondary_label Output]
2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3go-web-service LoadBalancer 10.245.107.189 203.0.113.20 80:30533/TCP 10m
4kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 3h4m
您可能有一个以上的服务正在运行,但可以找到一个标记为go-web-service
。 查找EXTERNAL-IP
列,并复制与go-web-service
相关的 IP 地址。 在此示例输出中,此 IP 地址为203.0.113.20
。 然后将 IP 地址粘贴到浏览器的 URL 栏中,以查看运行在您的 Kubernetes 集群上的应用程序。
注意:当Kubernetes以这种方式创建负载平衡器时,它会以非同步的方式创建负载平衡器。因此,在运行应用
命令后,Kubectl get services
命令的输出可能会显示LoadBalancer
的EXTERNAL-IP
地址在运行应用
命令应用
命令后仍处于等待
状态一段时间。
负荷平衡器将接收在端口80
上的请求,并将其传送到您集群内运行的 pods 之一。
通过此,您创建了一个 Kubernetes 服务,加上负载平衡器,为您提供一个单一的稳定的应用入口点。
结论
在这个教程中,你建造了 去应用,把它和多克一起装入容器,然后将其部署在一个库伯涅特集群. 然后,您创建了一个负载平衡器,为这个应用程序提供一个有弹性的切入点,确保即使您的集群中的某个节点失败,它仍然可以大量使用. 您可以使用此教程来部署自己的 应用到 Kubernetes 集群, 或者继续学习其他 Kubernetes 和 Docker 概念, 并使用您在步骤 1 中创建的样本应用程序 .
继续前进,你可以 将你的负载平衡器的IP地址地图到你所控制的域名,这样你可以通过人类可读的网址访问应用程序,而不是负载平衡器的IP。
最后,如果你想了解更多关于Go的信息,我们鼓励你查看我们的系列在 如何在Go中编码。