在 Google Cloud 上运行的 Container-Optimized OS 上设置 Docker 容器
哇,这个标题看起来像满嘴巴是不是?那么为什么要这样做呢?没有 Kubernetes 可以运行 Docker 容器吗?好吧,有时我只需要运行一个容器,我真的不想增加必须维护另一个 Kubernetes 集群的开销。蹩脚的借口,但这适用于我不需要完整 Kubernetes 集群功能的小型安装。
容器优化操作系统 (COS) 到底是什么?好吧,这个操作系统是由谷歌构建的,旨在优化成为 Kubernetes 集群的基本操作系统,甚至只是一个简单的 Docker 容器。它建立在 Chromium OS 之上,并且从一开始就融入了许多安全功能。例如,根文件系统以只读方式挂载。所以系统上的二进制文件不会改变,也不能改变。继续阅读有关它的所有信息,该项目是一个很棒的项目。
这是我将在这篇博文中向您展示的内容。在这篇博文中,我们将使用一个基本的Nginx Docker 映像作为我们要运行的容器。我们将首先使用gcloud命令提供的一些快捷方式在此映像上运行容器。然后,我们将介绍从私有存储库获取映像并运行所需的内容。让我们开始吧!
所有源代码都是托管在 github上,所以如果你不想也不必重新输入。
这篇文章将假设您已经设置了一个谷歌云帐户并且已经创建了一个谷歌云项目。如果您还没有遵循快速入门教程,请返回。
警告:我将展示在 Google Cloud 环境中创建资源的命令。这意味着真正的资源需要花费真金白银。如果您不想被收费,请小心。
让gcloud做所有的工作
让我们做一些非常基础的事情,我们将使用gcloud命令启动一个容器,在 COS VM 之上运行一个容器。这是gcloud命令的样子:
gcloud compute instances create-with-container nginx-vm --container-image nginx:1.15.8 --tags http-traffic
记下运行该命令后吐出的外部 IP 地址。我们将在下一步中需要它。
接下来,我们需要使用防火墙规则公开该实例:
gcloud compute firewall-rules create allow-http-traffic --target-tags http-traffic --allow tcp:80
完成后,继续尝试点击该图像的外部 IP 地址。您现在应该会看到“欢迎使用 nginx!”屏幕。
在下一部分之前,如果要删除该图像,请继续运行gcloud compute instances delete nginx-vm。
好吧,这看起来并不难,不是吗?我们可以从 docker hub 获取图像并将它们向上推送,然后 bam,我们有一个正在运行的 docker 容器在云中运行。但是,如果我们想要我们自己的代码,或者不托管在 Docker hub 上的代码,会发生什么?让我们创建一个私有 docker 镜像,并向您展示如何验证和拉取该镜像。
设置自定义 Docker 映像
让我们建立一个我们希望在 COS 镜像上运行的私有 docker 镜像。我们将获取基础 nginx 映像,并使用自定义索引页面对其进行修改,以演示我们可以构建和部署自定义映像。
这是您需要的两个文件,首先是新的index.html文件:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to My Custom nginx!</h1>
</body>
</html>
这是Dockerfile的样子:
FROM nginx:1.15.8
COPY index.html /usr/share/nginx/html
如果您想在本地进行测试,请运行docker build -t test . && docker run --rm -p 8080:80 test并将浏览器指向http://localhost:8080,您应该会看到“欢迎使用我的自定义 nginx!”横幅。
现在让我们获取我们的图像并将其推送到私有存储库中。这有点超出了这篇博文的范围,但请继续选择可以上传可以通过公共互联网访问的 docker 镜像的地方。我使用过 gitlab,但任何注册表都可以正常工作。只要确保它对公众开放。如果您在这方面需要帮助,请与我联系,我会尽力提供帮助。
使用注册表进行身份验证
现在我们已经在注册表中建立了我们的映像,我们需要一些方法来在我们的 VM 上对该注册表进行身份验证。如果您在本地执行此操作,我敢打赌您在某个时候运行了docker login命令以通过该注册表进行身份验证。好吧,这也正是我们要对这个 VM 做的事情。
那么我们如何运行该命令呢?好吧,COS 安装了一个名为cloud-init的工具包。Cloud-init是一套管理云镜像的工具。这些帮助提供了创建启动脚本的方法,以使您的云映像以您想要的方式运行。它们用于大多数云提供商,并且大多数发行版都有可以使用的钩子或工具。对于这种特殊情况,我们将有兴趣提供可以在启动时运行的用户脚本。为此,我们将提供一个user-data变量来提供cloud-config块。该块将创建一个用户、一个服务定义,并运行一个启动脚本来启动该服务。听起来很容易对吧?好吧,我们走吧!
下面我们来看看这个cloud-config配置文件可能是什么样子的:
#cloud-config
users:
- name: cloudservice
uid: 2000
write_files:
- path: /etc/systemd/system/myservice.service
permissions: 0644
owner: root
content: |
[Unit]
Description=My Service
Requires=docker.service network-online.target
After=docker.service network-online.target
[Service]
Environment="HOME=/home/cloudservice"
ExecStartPre=/usr/bin/docker login %REGISTRY% -u %USERNAME% -p %PASSWORD%
ExecStart=/usr/bin/docker run --network=host --rm --name=myservice %DOCKER_IMAGE%
ExecStop=/usr/bin/docker stop myservice
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
runcmd:
- iptables -A INPUT -p tcp -j ACCEPT
- systemctl daemon-reload
- systemctl enable --now --no-block myservice.service
这个配置文件被用作一个模板,它将为我们的cloud-config进程生成一个配置文件。让我们指出一些更重要的行,然后我将解释变量是什么:
第 1 行:这会将这个文件标记为云配置文件。如果你没有这个,系统将不会拾取并处理它。以这种方式拥有它是非常重要的。
第 3 - 5 行:设置专用于运行此服务的用户。这将创建一个安全沙箱,因为除了运行此容器外,该用户不会拥有任何权限。
第 7 - 26 行:这是/etc/systemd/system/myservice.service的文件内容。正如路径所示,这是一个systemd服务文件,它将运行我们的 docker 容器。此部分包含有关文件权限的信息以及嵌入在此配置中的文件的实际内容。
第 12 - 15 行:设置服务的描述以及服务需要运行的任何要求。在这种情况下,我们希望网络在线并且 docker 在此服务启动之前运行。
第 17 - 23 行:设置服务的启动和停止方式。这是一些更重要的位。首先,我们将主目录设置为我们之前创建的用户。这种沙箱和隔离运行的地方。接下来,ExecStartPre是其中一些魔法开始发挥作用的地方。这是执行docker login命令的地方。当前所有参数都是变量,可以替换,但这就是神奇之处。接下来ExecStart实际运行docker run命令。需要注意的一件重要事情是--network=host标志。我们希望 docker 容器使用主机的网络接口,而不是创建正常的隔离网络堆栈。这样,容器就可以像网络上的主机一样工作,并且暴露所有端口,而无需任何进一步的魔法。
第 22 - 23 行:这会将服务设置为在失败时重新启动,并在每次重试之间等待 10 秒
第 28 - 31 行:此部分在读取配置并完成所有其他部分后运行。这就像一个启动脚本。首先,我们设置内部防火墙以使用iptables接受 TCP 连接。然后我们重新加载systemd守护程序以读取我们的配置文件,然后我们启用并排队启动我们的服务。需要注意的是,--now --no-block命令在这里很重要,因为我们希望服务现在启动,但我们也希望它在systemd进程中排队,以便等待 Docker 服务和网络连接在线。否则我们的容器可能无法正确启动,因为 Docker 还没有准备好,或者我们无法访问互联网来获取我们的容器。
现在,所有这些变量的用途是:
%REGISTRY%:这定义了注册表所在的根域。例如,在测试这个时,我使用了 gitlab,所以注册表应该是registry.gitlab.com。
%USERNAME%:这是登录的用户名。与 gitlab 主题保持一致,我使用了一个部署令牌,用户名是 gitlab 给我的。
%PASSWORDD%:这是可用于登录的密码或令牌。
%DOCKER_IMAGE%:这是 docker 镜像路径。 IE。registry.gitlab.com/myimage/test:1。
现在,我该如何运行它?好吧,用正确的值替换所有变量,然后运行以下命令:
gcloud compute instances create test-nginx --image cos-stable-72-11316-136-0 --image-project cos-cloud --tags http-traffic --metadata-from-file user-data=cloud-init-config.yml
如果您使用该命令吐出的 IP 地址,您应该会看到“欢迎使用我的自定义 nginx!”横幅。如果没有,请阅读故障排除部分。如果一切正常,恭喜!
故障排除注意事项
好吧,该网站没有出现。怎么办?好吧,让我们先通过 SSH 进入盒子,这样我们就可以开始调查发生了什么。为此,让我们通过运行以下命令来确保打开防火墙:gcloud compute firewall-rules create allow-ssh-traffic --allow tcp:22。这应该会打开防火墙以允许您通过执行以下命令gcloud compute ssh <box name, i.e. test-nginx>来通过 SSH 进入该框。
既然你在盒子上,你到底在找什么?好吧,对于我们的 docker 示例,我们希望确保 docker 容器启动并运行,因此我们将发出docker container ls命令来验证它是否启动并运行。如果它已启动并正在运行,那么您可能遇到了防火墙问题。访问控制台并检查网络接口并查看入口是否设置正确。我们希望看到的是端口 80 是开放的并且工作正常。如果是,则iptables可能没有按预期运行,因此请验证 VM 实例本身,您可以执行类似curl localhost的操作。如果可行,请验证iptables是否正确设置。
但是如果容器甚至没有运行呢?我从那里去哪里?好吧,我们需要调查日志并开始筛选可能发生的事情。为此,您开始使用sudo journalctl命令。这将为您提供盒子启动时的所有日志。我们示例的另一个命令是sudo systemctl status myservice,它向您显示服务的原始状态,是启动还是关闭,以及服务现在可能发生的情况。
我遇到的另一件事是首先要检查云初始化脚本是否正确运行。一个很好的检查方法是验证我们期望的文件是否在那里。在这个例子中,我们希望看到一个/etc/systemd/system/myservice.service的文件。如果不存在,通常问题是第一行没有准确地读取#cloud-config。仔细检查您的配置并查阅日志以进行user-data解析和检索。通常有一条日志消息表明该文件无法从变量中理解并忽略数据。
结论
希望我已经让您了解了如何使用 Google Cloud 上的 Container-Optimized OS 设置单个 Docker 容器实例。当我真的不需要 Kubernetes 实例的所有马力时,我发现这是一个有用的功能。这种用于几个容器的重量更轻的基础设施提供了有用且具有成本效益。但也请注意,这些实例无论如何都不会被复制,所以如果一个发生故障,它就会全部故障。在云中要小心,因为每个堆栈都应该建立在云资源可以随意进出的假设之上。
所有这些代码和示例都是托管在我的 github 帐户上。
更多推荐



所有评论(0)