介绍
在下一个教程中,我们将通过 docker compose 将我们的 dockerized PHP 应用程序部署到“生产” ,并将在GCP(谷歌云平台)上创建这个“生产”环境。本教程作为 GCP 的入门知识,以建立一些基础知识,因为我们将使用该平台提供运行我们的 dockerized PHP 应用程序所需的所有基础设施。
在此过程中,我们将了解GCP 项目作为我们自己在 GCP 中的“空间”,并将服务帐户作为一种以编程方式进行通信的方式。我们将从通过 UI 手动执行所有操作开始,但还将解释如何通过gcloudcli以编程方式执行此操作,并以完全自动化的脚本结束。
以下视频显示了整个流程:
我使用的 API 密钥(请参阅服务帐户密钥文件)不在存储库中,因为我会为任何使用付费,即,您必须创建自己的项目和密钥才能跟进。
警告
但是您仍然应该预先了解这一点,并确保关闭所有内容/删除所有内容,以防您自己尝试。“最安全”的方法是关闭(删除)整个项目。
设置 GCP 项目
在 GCP 上,资源被组织在所谓的项目下。我们可以通过Create Project UI创建一个项目:
项目ID必须是全局唯一的string
,我pl-dofroscra-p
为本教程选择了(pl
=> Pascal Landau;dofroscra
=> Docker From Scratch;p
=> 生产)。
创建服务帐户
下一步,我们需要一个可以用来发出 API 请求的服务帐户,因为我们不想使用我们的“个人 GCP 帐户”。服务帐户是通过IAM & Admin > Service Accounts UI创建的:
创建服务帐户密钥文件
为了以编程方式使用该帐户,我们还需要通过选择相应服务帐户的“管理密钥”选项来创建一个密钥文件。
$serviceAccountId
是服务帐户的数字 ID,例如 109548647107864470967
. 要创建密钥:
- 单击
"ADD KEY"
并"Create new key"
从下拉菜单中选择- 这将打开一个模式窗口来选择键类型。
- 选择推荐的 JSON 类型并单击
"Create"
。- 然后 GCP 将生成一个新的密钥对,存储公钥并提供下载的私钥文件。
- 下载文件并确保像对待任何其他私钥(ssh、gpg、...)一样对待它,即永远不要公开分享它!
我们将此文件存储在代码库的根目录gcp-service-account-key.json 中并将其添加到.gitignore
文件中。
每个服务帐户还有一个唯一的电子邮件地址,由其(非数字)id
和project id
. 也可以直接在密钥文件中找到:
配置 IAM 权限
IAM代表身份和访问管理 (IAM),用于管理 GCP 上的权限。两个核心概念是“权限”和“角色”:
- 权限对于特定操作是细粒度的,例如
storage.buckets.create
“创建云存储桶” - 角色组合了一系列权限,例如,
Cloud Storage Admin
角色具有以下权限storage.buckets.create
storage.buckets.get
- 等等
- 角色分配给用户(或服务帐户)
您可以在Permissions Reference中找到所有权限的完整概述以及了解角色 > 预定义角色下的所有角色。
在本教程中,我们将为服务帐户“ user
”分配以下角色docker-php-tutorial-deployment@pl-dofroscra-p.iam.gserviceaccount.com
:
- 存储管理员
- 秘密经理管理员
- 计算管理员、服务帐户用户和 IAP 安全隧道用户
- 通过 IAP 登录 VM是必需的。
可以通过Cloud Console IAM 界面通过编辑相应的用户来分配角色。
注意: IAM 权限的更改可能需要一些时间(通常是几秒钟)才能生效。
设置 gcloud CLI 工具
GCP的CLI 工具称为 gcloud,适用于所有操作系统。
在本教程中,我们使用“捆绑 Python ”选项通过GoogleCloudSDKInstaller.exe在 Windows 上本地安装的版本 380.0.0。
仅供参考:如卸载 Google Cloud CLI中所述,您可以通过以下方式找到安装和配置目录:
<span style="color:#000000"><span style="background-color:#fbedbb"># installation directory
$ gcloud info --format='value(installation.sdk_root)'
C:\Users\Pascal\AppData\Local\Google\Cloud SDK\google-cloud-sdk
# config directory
$ gcloud info --format='value(config.paths.global_config_dir)'
C:\Users\Pascal\AppData\Roaming\gcloud </span></span>
我不会使用我的个人 Google 帐户来运行gcloud
命令,因此我不会通过运行gcloud init. 相反,我将使用我们之前创建的服务帐户并按照gcloud auth activate-service-account中的描述激活它
我们将在下一个教程部分中使用 docker compose 来运行我们的 PHP 应用程序,并且需要使我们的 docker 镜像在容器注册表中可用。幸运的是,GCP 提供了一个 Container Registry 产品,它为我们提供了一个现成的私有注册表,作为 GCP 项目的一部分。在我们可以使用它之前,必须启用相应的Google Container Registry API :
您可以在 Cloud Console 界面中的 Container Registry 下找到Container Registry。
验证 Docker
由于 Container Registry 是私有的,我们需要在推送 docker 镜像之前进行身份验证。GCP 文档“Container Registry Authentication methods”中描述了可用的身份验证方法。为了从我们的本地主机系统推送图像,我们将使用我们之前创建的服务帐户密钥文件并运行文档的“JSON 密钥文件”部分中显示的命令。
那么当我们运行这个命令时到底“发生”了什么?根据docker login 文档
当您登录时,该命令将凭据存储在 Linux 上的$HOME/.docker/config.json或 Windows 上的%USERPROFILE%/. docker/config.json [...]
Docker 引擎可以将用户凭据保存在外部凭据存储中,例如操作系统的本机钥匙串。[...]
您需要在$HOME/.docker/config.json中指定凭证存储来告诉 docker 引擎使用它。[...]
默认情况下,Docker 在每个平台上查找本机二进制文件,即
osxkeychain
在 macOS 上查找“wincred
”,在 Windows 上查找“pass
”,在 Linux 上查找“ ”。
换句话说:我将无法在任何地方以“纯文本”形式查看服务帐户密钥文件的内容,但 Docker 将利用操作系统特定的工具来安全地存储它们。在 Windows 上运行命令后,我在~/.docker/config.jsondocker login
中找到了以下内容:
在本教程中,我们将创建一个超级简单的nginx
alpine 图像,通过以下方式提供“Hello world” hello.html文件:
<span style="color:#000000"><span style="background-color:#fbedbb">docker build -t my-nginx -f - . <<EOF
FROM nginx:<span style="color:#000080">1</span>.<span style="color:#000080">21</span>.5-alpine
RUN echo <span style="color:#800080">"</span><span style="color:#800080">Hello world"</span> >> /usr/share/nginx/html/hello.html
EOF</span></span>
图像的名称是my-nginx
:
<span style="color:#000000"><span style="background-color:#fbedbb">$ docker image ls | grep my-nginx
my-nginx latest 42dd1608d126 <span style="color:#000080">50</span> seconds ago <span style="color:#000080">23</span>.5MB </span></span>
为了将映像推送到注册表,映像名称必须以相应注册表为前缀。这让我很困惑,因为我本来希望能够运行这样的东西:
<span style="color:#000000"><span style="background-color:#fbedbb">$ docker push my-nginx --registry=gcr.io
unknown flag: --registry
See <span style="color:#800080">'</span><span style="color:#800080">docker push --help'</span>. </span></span>
但是不,没有这样的--registry
选择。更糟糕的是:省略它会导致推送到docker.io
“默认”注册表:
<span style="color:#000000"><span style="background-color:#fbedbb">$ docker push my-nginx
Using <span style="color:#0000ff">default</span> tag: latest
The push refers to repository [docker.io/my-nginx] </span></span>
根据有关推送和拉取图像的 GCP 文档,将图像推送到 GCP 注册表需要执行以下步骤:
- 使用 Container Registry 中的目标路径标记映像,包括gcr.io注册表主机和项目 ID my-project
- 将镜像推送到注册表
在我们的例子中,我们的 Container Registry 的目标路径是:
<span style="color:#000000"><span style="background-color:#fbedbb">gcr.io/pl-dofroscra-p </span></span>
因为pl-dofroscra-p
是我们之前创建的 GCP 项目的 id。
完整的图像名称变为:
<span style="color:#000000"><span style="background-color:#fbedbb">gcr.io/pl-dofroscra-p/my-nginx </span></span>
要推送my-nginx
图像,我们必须首先通过以下方式“添加另一个名称”docker tag:
<span style="color:#000000"><span style="background-color:#fbedbb">$ docker tag my-nginx gcr.io/pl-dofroscra-p/my-nginx
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my-nginx latest ba7a2c5faf0d <span style="color:#000080">15</span> minutes ago <span style="color:#000080">23</span>.5MB
gcr.io/pl-dofroscra-p/my-nginx latest ba7a2c5faf0d <span style="color:#000080">15</span> minutes ago <span style="color:#000080">23</span>.5MB </span></span>
并在之后推送该名称:
<span style="color:#000000"><span style="background-color:#fbedbb">$ docker push gcr.io/pl-dofroscra-p/my-nginx
Using <span style="color:#0000ff">default</span> tag: latest
The push refers to repository [gcr.io/pl-dofroscra-p/my-nginx]
134174afa9ad: Preparing
cb7b4430c52d: Preparing
419df8b60032: Preparing
0e835d02c1b5: Preparing
5ee3266a70bd: Preparing
3f87f0a06073: Preparing
1c9c1e42aafa: Preparing
8d3ac3489996: Preparing
8d3ac3489996: Waiting
3f87f0a06073: Waiting
1c9c1e42aafa: Waiting
cb7b4430c52d: Pushed
134174afa9ad: Pushed
419df8b60032: Pushed
5ee3266a70bd: Pushed
0e835d02c1b5: Pushed
8d3ac3489996: Layer already exists
3f87f0a06073: Pushed
1c9c1e42aafa: Pushed
latest: digest: sha256:0740591fb686227d8cdf4e42b784f634cbaf9f5caa6ee478e3bcc24aeef75d7f
size: <span style="color:#000080">1982</span> </span></span>
然后,您可以在 Container Registry的 UI 中找到该映像:
不用担心:我们不必每次推送前都进行标记,因为我们将设置为make在下一部分构建图像时自动使用正确的名称。
图像存储在 Google 云存储桶中
我们将角色分配给Storage Admin之前包含storage.buckets.create
权限的服务帐户。如果我们不这样做,就会发生以下错误:
<span style="color:#000000"><span style="background-color:#fbedbb">denied: Token exchange failed for project 'pl-dofroscra-p'.
Caller does not have permission 'storage.buckets.create'.
To configure permissions, follow instructions at:
https://cloud.google.com/container-registry/docs/access-control </span></span>
Container Registry 尝试将 docker 镜像存储在 Google Cloud Storage 存储桶中,该存储桶在推送第一个镜像时动态创建,请参阅GCP 文档“添加注册表”:
第一个图像推送到主机名会触发在项目中创建注册表和相应的 Cloud Storage 存储桶。此初始推送需要项目范围的权限才能创建存储桶。
您可以找到存储桶,在我的例子中,它在artifacts.pl-dofroscra-p.appspot.com
Cloud Storage UI中命名: