在这篇博客里,我想介绍一个“不同”的工作方式与OpenShift,使用Node.js来创建一个Web应用程序使用OpenShift。
在这篇博客里,我想介绍一个“不同”的工作方式与OpenShift。在典型的方式来部署一个蚕茧OpenShift,我们有一套非常有用的对象;我们已建立/图像配置。这就隐藏了图像构建的细节,让我们感到痛苦,但有时我们只想看到云中运行的代码。或者,我们想看看我们的服务/应用程序是否能够与附近的服务交互,或者我们有一些代码,但我们还不想使用Git回购协议。为了解决这个问题,我将展示initcontainers的概念,以及如何通过创新点我们实现一些很酷的东西,像部署我们的代码里面运行的容器。
入门
本指南是取决于你使用的OpenShift安装或您已经安装使用minishift或使用OC集群本地机器OpenShift。
一旦进入,登录。
我们的工作空间配置
一旦你有了OpenShift运行起来,你已经登录,下一步是创建一个项目:
oc new-project my-project
图像
我们需要得到节点。js配置了我们需要工作的工具。要导入它,我们需要一个ImageStream对象,它将抓取我们的图像,并使它可以访问我们的项目。
oc import-image cvr-node:latest --from=docker.io/cvaldezr/nodejs --confirm
这将会在mhart/阿尔卑斯图像中抓取这个映像,其中包括节点、NPM、no恶魔,以及所有必要的工具来构建本地插件。该图像仅为89MB,因此部署速度非常快。
模板
接下来,我们需要获取Pod的模板定义。稍后我会详细解释这个结构。
curl -o pod.yml https://gist.githubusercontent.com/cesarvr/2dedd0bb912be441aa98b67e1ac4bcc6/raw/2cf75a5512014fd40086375d5a46c81940c53fc8/pod.yml
获得该文件后,您需要修改第12行并为您的图像添加URL。你可以通过以下方式获取URL:
oc get is #<DOCKER REPO is the url we need to copy>
这就是模板的外观。你可以看到它很好很短:
apiVersion: v1
kind: Pod
metadata:
name: node-dev
labels:
app: node-js-dev
spec:
containers:
- name: nodejs
image: 172.30.1.1:5000/devel/cvr-node
command: ['/bin/sh', '-c']
args:
- cd /app/;
echo folder:$PWD;
npm install;
nodemon $(node -e "console.log(require('./package.json').main)")
volumeMounts:
- mountPath: /app
name: app-volume
- mountPath: /.npm
name: npm-cache
ports:
- containerPort: 8080
initContainers: # This is the init container it will wait until app/ folder is in sync.
- name: folder
image: busybox
command: ['/bin/sh', '-c']
args: ['until [ "$(ls -A ./app/)" ]; do echo "waiting for user to push..."; sleep 2; done']
volumeMounts:
- mountPath: /app
name: app-volume
volumes:
- name: app-volume
emptyDir: {}
- name: npm-cache
emptyDir: {}
查看要点的代码。
接下来要做的是使用我们的模板创建我们的Pod。
oc create -f pod.yml
为了检查状态,我们可以使用这个命令。
oc get pods
我们应该看到创建是成功的,如果不是,请确保模板有来自您的ImageStream的正确的图像URL,并且您有权限将它导入到您的项目中。
编写一些代码
现在是有趣的部分。让我们在Node.js中编写一个小的hello world服务器应用程序。
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!!!'))
app.listen(8080, () => console.log('Example app listening on port 8080!'))
将此文件保存为app.js,请访问包。json,设置"主"属性。您将看到,模板配置正在寻找该属性来定位和执行应用程序的入口点。你可以改变并改进它以满足你的需要。
{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\"exit 1",
"start": "node app.js"
},
"author": "",
"license": "GPL",
"dependencies": {
"express": "^4.16.2"
}
}
使用npm安装express安装依赖项——保存,但只用于在我们的package.json中注册依赖项。
部署
首先,我们需要将文件发送到Pod,在我的例子中,它被命名为node-dev。你可以使用oc来检查你的名字。
oc rsync -c folder . node-dev:app/
打开我们的Pod。
oc expose pod node-dev --port=8080
oc expose service node-dev
现在访问您的新创建的服务。
oc get route -o wide
node-dev-devel.127.0.0.1.nip.io
修改
现在让我们来改变一些。
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hola Mundo!!!'))
app.listen(8080, () => console.log('Example app listening on port 8080!'))
修改完成后,转到控制台并写入:
oc rsync . node-dev:app/
现在在浏览器中刷新一下。
注意,在我们的例子中,我们没有使用-c文件夹,这是因为我们现在针对的是运行时容器(稍后我会详细解释这一点)。
刚才发生了什么?
为了解释所发生的事情,让我们快速查看一下模板。
apiVersion: v1
kind: Pod
metadata:
name: node-dev
labels:
app: node-js-dev
在这里,我们定义了pod的名称和标签,没有什么特别有趣的。
我们的node.js运行时容器
spec:
containers:
- name: nodejs
image: 172.30.1.1:5000/devel/cvr-node
command: ['/bin/sh', '-c']
args:
- cd /app/;
echo folder:$PWD;
npm install;
nodemon $(node -e "console.log(require('./package.json').main)")
volumeMounts:
- mountPath: /app
name: app-volume
- mountPath: /.npm
name: npm-cache
ports:
- containerPort: 8080
这是主控制舱。正如您所看到的,它使用的是我们前面导入的oc导入图像的图像。我还包括一些启动Pod命令sh -c,它将使用args运行一些shell命令。基本上,它会进入应用程序/文件夹运行npm安装并启动nodemon,但如果我们这样做并发布一个图像,它会立即崩溃,因为nodemon无法找到任何东西。如果我们有办法等待,直到我们的挂载点有一些文件,我们可以避免一个无限的崩溃循环。
使用InitContainer的解决方案
Pods对象有这个神奇的功能,叫做initcontainer,这意味着你可以有一个容器来为你做一些初始化工作。当您想要运行一个轻量级容器和一个装满编译工具的包(例如,如果您想要一个包含所有编译/构建工具的InitContainer,然后是一个运行时容器,只有一个非常简单的容器,只需要运行一些必需的工具)时,这将非常有用。
initContainers: # This is the init container will wait until app/ folder is in sync.
- name: folder
image: busybox
command: ['/bin/sh', '-c']
args: ['until [ "$(ls -A ./app/)" ]; do echo "waiting for user to push..."; sleep 2; done']
volumeMounts:
- mountPath: /app
name: app-volume
这就是我们的InitContainer的样子。我只选择一个很小的图像,Busybox,并运行一个小脚本,以阻止PodInit状态下的Pod执行。
如果您很好奇,您可以通过oc log -c文件夹node-dev -f来获取这个Pod的日志。您将看到“等待用户推动…”“每两秒钟,当你运行oc rsync -c文件夹时。nodedev:app/,您正在与InitContainer同步,并通过这样做,直到["$(ls -A ./app/)]”);不再是正确的,它将终止与InitContainer关联的sh命令。
结论
我在寻找使用Openshift/Kubernetes的创造性方法方面有很多乐趣,所以我希望您能找到有用的模板,您可以将它调整到您自己的用例中,或者更好地改进它。另外,我使用了Node。因为我在日常工作中使用了这种语言,但是我认为在Java中实现这一点没有任何问题。如果运行时容器只是一个等待EAR的JVM,在某个目录中WAR(可能我有点过时),然后在每次文件系统发生变化时都进行热部署,这将非常酷。
另外,我想补充的是,这种方法不是水平规模友好的,或者基本上您需要将代码推进到每个Pod,因为在这个示例中,我只是使用了容器的文件系统。您可以通过将文件系统设置为PVC(持久的容量索赔)来克服这种限制,然后将它与您的容器共享,有一些挑战,但我认为它可以工作,但这是另一篇文章。
了解更多关于init容器的信息。
无论你是新到容器还是有经验,下载这个小抄可以帮助你遇到你最近没有完成的任务。