# 容器虚拟机
# 概述
如果您的应用的依赖包比较复杂,无法直接上传或者通过新浪云的云端构建程序构建的时候,您可以通过线上手工部署的方式来部署您的程序,手工部署的过程和使用云主机、VPS等差不多,下文中我们将一步一步教您如何在新浪云手工部署一个应用。
请注意
手工部署主要用来在线构建容器镜像、运行调试,线上环境请一定要打成镜像后再部署。
# 工作流程示意图
容器虚拟机的工作流程如下图所示:
- 容器虚拟机本身是一个运行特定操作系统的容器,包含5GB磁盘空间,如果有更多的磁盘需求可以挂载本地存储或者共享存储;
- 如果容器虚拟机对外提供web、websocket服务,需要将以上服务暴漏在0.0.0.0的5050端口,外层负载均衡器会将请求转发到这个端口;
- 容器虚拟机本身使用SSH管理。
# 最大实例数量
容器虚拟机最多启动1个实例,如果需要多个实例分部署运行,请将虚拟机打包为镜像,通过创建镜像类应用运行这里创建的镜像。
说明
如果在多个实例运行的系统本身应该遵循分布式系统的要求,临时产生的文件不能有强依赖性,且永久存储的文件应该写到网络磁盘中,而不是本地。
# 创建应用
登录云应用管理平台https://sae.sinacloud.com (opens new window),如果没有账号请先注册;
点击“+创建应用”进入应用的创建页面;
开发语言选择“容器虚拟机”;
选择一个操作系统及操作系统的版本;
选择单实例的配置;
填写应用的二级域名前缀和应用名称;
# 管理应用
在创建完成后,进入应用的管理中心,左侧选择“运行环境管理”,进入“容器管理中心”:
# 通过Web Console管理
点击操作中的“终端”进入Web Console:
在Web终端中可以执行命令:
# 通过SSH客户端管理
容器虚拟机的应用也可以通过SSH客户端管理,从“容器管理”页面点击“SSH登录”可以查询应用的SSH主机地址、端口、用户名;当前仅支持通过SSH密钥方式登录,详情请参考SSH客户端章节。
# 限制
# 允许的Cap
容器虚拟机有以下的Cap分类权限:
SETUID
SETGID
CHOWN
DAC_OVERRIDE
SETFCAP
FOWNER
NET_BIND_SERVICE
KILL
SYS_PTRACE
AUDIT_WRITE
除以上权限外,其他所有的Cap都不可以使用,因此诸如/usr/sbin/apachectl start
或者/bin/systemctl restart httpd.service
等启动命令都会被禁止,可以找到软件的绝对路径启动。
说明
例如apache软件安装在/usr/sbin/httpd
绝对路径,可以使用/usr/sbin/httpd -k start
直接启动。
# Mint 进程管理工具
# 说明
Mint是新浪云为所有的容器虚拟机提供的进程管理工具,可以在任何容器虚拟机实例中直接使用,Mint 的配置文件为 /etc/Procfile ,Procfile 中每一行定义一个要启动的进程,格式为:
进程名:启动命令
例如:
web: go run web.go -a :$PORT
worker: bundle exec ruby worker.rb
请注意
冒号后需要有空格,此文件为yaml格式。
# 支持的命令
Mint 支持如下命令:
[root@douyu1 /]# mint
Usage:
mint reload # Reload Procfile
mint start PROC # Start a Process
mint stop PROC # Stop a Process
mint restart PROC # Restart a Process
mint status # Print Processes' status
mint check # Show entries in Procfile
请注意
Mint 会自动重启异常退出的进程,但是如果进程连续 3 次启动不到 1 分钟就退出,Mint 会认为进程无法启动而不再重启。
# Crontab 定时任务
容器虚拟机默认没有安装crond (opens new window),如果你需要使用容器虚拟机的 crontab 功能,您需要安装并运行 crond 进程。(以 CentOS 为例):
yum install cronie
将 crond 启动命令加入到 /etc/Procfile
:
crond: crond -n
最后,执行 mint start crond
启动 crond
进程。
# 打包镜像
您可以将容器虚拟机的环境及文件打包为一个容器的镜像,打包后的镜像可以用于新的应用启动时的参数,也可以用于备份。
# 启动命令
启动命令用于docker的CMD
指令,在指令格式上,一般推荐使用 exec
格式,这类格式在解析时会被解析为 JSON
数组,因此一定要使用双引号 ",而不要使用单引号。
如果使用 shell
格式的话,实际的命令会被包装为 sh -c
的参数的形式进行执行。比如:
echo $HOME
在实际执行中,会将其变更为:
[ "sh", "-c", "echo $HOME" ]
Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像真正的虚拟机、物理机里面那样,用 upstart/systemd
去启动后台服务,容器内没有后台服务的概念。
错误的启动命令
注意,以下是一个错误的启动命令示范。
service nginx start
然后发现容器执行后就立即退出了,此外还发现在容器内去使用 systemctl 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
而使用 service nginx start 命令,则是希望 upstart
来以后台守护进程形式启动 nginx
服务。而刚才说了service nginx start
会被理解为 CMD [ "sh", "-c", "service nginx start"]
,因此主进程实际上是 sh。那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。
正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:
nginx -g "daemon off;"
也可以使用默认命令/bin/mint master
,关于mint请参考mint-进程管理工具章节。
# 如何打包
- 点击“操作”区域的“打包镜像”按钮;
- 在弹出的框中输入“镜像名称”、“镜像版本”及“启动命令”,点击确认;
在打包过程中,容器的状态将变成“处理中”,等状态变成“运行中”时即表示打包已经完成;
从“镜像管理”标签,选择“所有镜像”分类,即可查询刚才创建的镜像
# 示例 - 安装openresty并对外提供HTTP服务
# 创建应用
从应用的创建页面创建一个“容器虚拟机”的应用,选择“centos”、“6.7”的操作系统:
# 进入容器管理页面
创建完成应用后,进入应用的管理中心:
# 进入在线终端
从“操作”中点击“终端”进入web terminal:
# 编译openresty前的准备工作
先执行yum clean all
清空本地的yum包列表缓存:
[root@v2centostest /]# yum clean all
Loaded plugins: fastestmirror
Cleaning repos: base extras updates
Cleaning up Everything
安装vim、perl、tar支持:
yum install -y vim tar perl
安装openresty的依赖包:
yum install -y pcre-devel openssl-devel gcc curl
下载openresty的源码:
cd /
mkdir openresty
cd openresty
curl 'https://openresty.org/download/openresty-1.13.6.2.tar.gz' -o openresty-1.13.6.2.tar.gz
解压压缩包:
tar -zxvf openresty-1.13.6.2.tar.gz
# 编译安装
开始编译:
cd openresty-1.13.6.2
./configure
看到以下输出为成功,否则请自行参考输出安装依赖包:
....
Configuration summary
+ using system PCRE library
+ using system OpenSSL library
+ using system zlib library
nginx path prefix: "/usr/local/openresty/nginx"
nginx binary file: "/usr/local/openresty/nginx/sbin/nginx"
nginx modules path: "/usr/local/openresty/nginx/modules"
nginx configuration prefix: "/usr/local/openresty/nginx/conf"
nginx configuration file: "/usr/local/openresty/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/openresty/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/openresty/nginx/logs/error.log"
nginx http access log file: "/usr/local/openresty/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
cd ../..
Type the following commands to build and install:
gmake
gmake install
编译和安装:
gmake && gmake install
有以下类似输出时说明安装成功:
...
cp conf/fastcgi.conf '/usr/local/openresty/nginx/conf/fastcgi.conf.default'
test -f '/usr/local/openresty/nginx/conf/uwsgi_params' \
|| cp conf/uwsgi_params '/usr/local/openresty/nginx/conf'
cp conf/uwsgi_params \
'/usr/local/openresty/nginx/conf/uwsgi_params.default'
test -f '/usr/local/openresty/nginx/conf/scgi_params' \
|| cp conf/scgi_params '/usr/local/openresty/nginx/conf'
cp conf/scgi_params \
'/usr/local/openresty/nginx/conf/scgi_params.default'
test -f '/usr/local/openresty/nginx/conf/nginx.conf' \
|| cp conf/nginx.conf '/usr/local/openresty/nginx/conf/nginx.conf'
cp conf/nginx.conf '/usr/local/openresty/nginx/conf/nginx.conf.default'
test -d '/usr/local/openresty/nginx/logs' \
|| mkdir -p '/usr/local/openresty/nginx/logs'
test -d '/usr/local/openresty/nginx/logs' \
|| mkdir -p '/usr/local/openresty/nginx/logs'
test -d '/usr/local/openresty/nginx/html' \
|| cp -R docs/html '/usr/local/openresty/nginx'
test -d '/usr/local/openresty/nginx/logs' \
|| mkdir -p '/usr/local/openresty/nginx/logs'
gmake[2]: Leaving directory `/openresty/openresty-1.13.6.2/build/nginx-1.13.6'
gmake[1]: Leaving directory `/openresty/openresty-1.13.6.2/build/nginx-1.13.6'
mkdir -p /usr/local/openresty/site/lualib /usr/local/openresty/site/pod /usr/local/openresty/site/manifest
ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/local/openresty/bin/openresty
# 修改openresty监听的端口
cd /usr/local/openresty/nginx/conf
vim nginx.conf
按下小写i
进入编辑模式,找到listen 80;
除,将80
改为5050
,CTRL+C
后输入:wq
保存退出。
# 将启动进程加到mint的管理中
openresty的启动方式为:
/usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
将以上启动脚本加到Procfile中:
vim /etc/Procfile
输入以下内容:
openresty: /usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
完整的文件内容和路径如下:
[root@v2centostest /usr/local/openresty/nginx/conf]# cat /etc/Procfile
openresty: /usr/local/openresty/nginx/sbin/nginx -g 'daemon off;'
reload mint:
mint reload
查看进程状态:
[root@v2centostest /usr/local/openresty/nginx/conf]# mint status
openresty RUNNING pid 8644, uptime 0:00:25
可以看到openresty的进程已经在执行中:
[root@v2centostest /usr/local/openresty/nginx/conf]# ps -ajxfww
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 13 13 13 pts/0 8653 Ss 0 0:00 /bin/bash
13 8653 8653 13 pts/0 8653 R+ 0 0:00 \_ ps -ajxfww
0 1 1 1 ? -1 Ssl 0 0:00 /bin/mint master
1 8644 8644 1 ? -1 S 0 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -g daemon off;
8644 8645 8644 1 ? -1 S 99 0:00 \_ nginx: worker process
# 访问应用
这是通过浏览器已经可以访问应用:
# 打包镜像
如果您的应用需要支持大流量访问,需要水平扩展,可以应用的环境打包为镜像,然后用一个“容器”分类的应用运行。
从容器的管理页面点击“操作”中的“打包镜像”:
输入镜像的信息:
说明
这里使用默认的/bin/mint master
为启动名称,进程托管服务将扫描/etc/Procfile
下的服务列表启动。
# 使用镜像启动新的应用
打包完成后就可以使用打包后的镜像启动新的应用,回到应用的创建页面,应用分类选择“Docker”,“部署方式”选择“镜像”,“镜像类型”选择“我的镜像”,从“选择镜像”中选择刚刚打包的镜像:
选择一个应用的二级域名,选择实例的数量:
说明
基于镜像的应用不同于容器虚拟机,可以启动多个实例,因此可以横向扩展实例数量支撑大流量业务。
创建完成后从容器管理页面可以看到新启动的实例:
从浏览器访问应用可以看到openresty的默认页面:
进入web terminal可以看到nginx的进程已经启动:
[root@fromimageexample /]# ps -ajxfw
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 16 16 16 pts/0 23 Ss 0 0:00 /bin/bash
16 23 23 16 pts/0 23 R+ 0 0:00 \_ ps -ajxfw
0 1 1 1 ? -1 Ssl 0 0:00 /bin/mint master
1 13 13 1 ? -1 S 0 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx -g daemon off;
13 15 13 1 ? -1 S 99 0:00 \_ nginx: worker process
# 计费说明
# 计费规则
计费的规则为:单实例价格 * 实例总数 * 实例的运行时长,按分钟计费。
其中单实例的价格可以从价格中心 (opens new window)查询。
例如单实例的配置为标准型,单价为0.06元每实例每小时,则一天的价格为:
0.06 * 24 = 1.44元
# 流量计费
当前容器虚拟机的HTTP、HTTPS流量不计费。
# 协议支持
应用支持http、https和websocket协议(ws、wss)。
# 端口映射
# 工作原理
应用的负载均衡(新浪云提供)监听80和443端口,然后将请求转发到容器中的5050端口,示意图如下:
# 支持的端口
容器中可以启动多个端口,但是仅5050端口会映射到负载均衡上,其他的端口只能在本地访问。可以从环境变量中获取支持的端口,环境变量的key为:
PORT
# 日志系统
# 容器日志分类
容器日志分类stdout
和stderr
分类,分别是标准输出和标准错误输出的捕获。
因为容器的磁盘空间有限,大量的写入可能会占满磁盘导致服务不可用,请从程序中配置日志输出路径,将日志写到stdout
和stderr
分类,其中:
/proc/1/fd/1
是stdout
的绝对路径/proc/1/fd/2
是stderr
的绝对路径
# 查询容器stdout日志
- 进入应用的列表页面https://sae.sinacloud.com (opens new window)
- 点击操作处的管理进入应用的管理中心
- 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
- 选择“容器日志”的“stdout”分类
# 查询容器stderr日志
- 进入应用的列表页面https://sae.sinacloud.com (opens new window)
- 点击操作处的管理进入应用的管理中心
- 左侧导航选择“日志及监控”,选择“日志中心”进入日志管理
- 选择“容器日志”的“stderr”分类
# 文件系统
# 本地文件系统
实例在创建时将会分配5GB的本地磁盘,用于存放程序的代码和系统文件等,你可以从程序往本地写文件,容器虚拟机本地的文件是持久化存储,在重启后文件不会删除。
# 挂载共享存储
共享存储服务是为容器云提供的分布式存储服务,利用多副本写入实现数据高可靠性,并提升读取性能,可以提供数十M/S的峰值读写性能。
您可以通过挂载,将对应的共享存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。
通过共享存储,还可以在不同的容器之间共享数据,实现跨容器的数据共享和交互。
有关共享存储的更多介绍,请参考共享存储章节,请参考以下步骤挂载共享存储:
- 从共享存储 (opens new window)管理面板创建一个共享存储;
- 从云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
- 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面;
- 点击“挂载管理”进入挂载管理页面;
- 点击“+新增挂载”,存储的分类选择“共享存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。
说明
挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。
- 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情:
挂载完成后,从容器的实例中可以看到挂载的路径:
共享存储中的文件支持FTP管理,详情请参考共享存储的FTP管理章节。
挂载完共享存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径下了。
# 挂载本地存储
本地存储服务是为容器云提供的本地问年间存储服务,可以和本地磁盘读写一样速度的磁盘。
您可以通过挂载,将对应的本地存储卷映射进容器中,是容器获得本地文件读写和数据持久化功能。
有关共享存储的更多介绍,请参考本地存储章节,请参考以下步骤挂载本地存储:
- 从本地存储 (opens new window)管理面板创建一个本地存储;
- 从云应用管理平台 (opens new window)应用列表页面点击“管理”进入应用的管理中心;
- 左侧导航选择“运行环境管理”,“容器管理”进入容器管理页面;
- 点击“挂载管理”进入挂载管理页面;
- 点击“+新增挂载”,存储的分类选择“本地存储”,从下拉列表选择刚刚创建的存储,输入一个挂载路径。
说明
挂载路径是系统的绝对路径,如果你挂载的路径在本地文件系统已经存在,则挂载后本地的文件系统将被覆盖。
- 选择“立即重启容器”后挂载会立即生效,挂载完成后可以看到挂载的详情:
挂载完成后,从容器的实例中可以看到挂载的路径:
本地存储中的文件支持FTP管理,详情请参考本地存储的FTP管理章节。
挂载完地存储到本地的路径后,就可以在程序中将需要存储的文件写到挂载的路径了。
← Golang运行环境 应用间内网调用 →