目的
做过Linux开发的工程师可能都会或多或少有过如下的经验:
- 需要至少两台开发机器,一台Windows电脑用来运行IDE和各种工具,一台Linux电脑用来做编译和测试。
- 两台机器同步代码很麻烦。拷贝容易出现错漏,samba共享又很慢(一个vimgrap运行半天)。
- Linux机器下混杂着各种SDK,环境变量,临时包,不知道什么版本的工程软件……最后谁都不敢动这个神奇的环境,因为离开这个环境软件就跑不起来,甚至编译都编不过。
- 测试团队想复制你的环境,或者你想复制测试的环境来复现bug?办不到……
这篇文章就是尝试来解决这个问题的。
- 首先,我们只需要一台Windows电脑,并且宿主机和Linux环境共享数据。
- 其次,它不慢,至少在主流的电脑配置上和你分开跑两台机器差不多。
- 再次,这个环境可很容易的清理、复制、移植
这个方案不适合什么人
- “老破小”(“老”电脑,“破”硬盘,“小”内存)
- Windows或Mac等非Linux环境的开发。或者桌面系统原本就是Linux。
- 喜欢折腾vagrant的,对虚拟机有偏见特别是性能和稳定性上的偏见的,或者开发环境很单纯很简单的。
对于直接上了docker Windows版的,可以忽略VirtualBox部分。Docker目前已经原生支持Windows,但是有两个条件:1是必须是Windows 10 Enterprise版本,Home版不带hyper-v;2是必须打开hyper-v选项。
我之所以采用Virtualbox方案,是为了兼容Windows10 home版,以及关掉hyper-v。因为我需要跑的一个android虚拟机跟hyper-v冲突。
再说说vagrant。vagrant其实就是在虚拟机(比如VirtualBox)的基础上再封装了一层管理脚本,以及提供了许多预配置的虚拟机镜像文件一个命令就可以下载+安装+启动。本身是很好的工具,批量部署很有优势,我也折腾过两天。但个人感受有几个缺点:比如引入了不必要的复杂性,比如几个流行的Linux发行版的镜像更新很不及时,比如自动化脚本出错还得手动分析问题等等。因此放弃。
思路
方案的大体思路如下:
- 首先宿主机器是一台Windows。当然Mac甚至Linux也可以,但不在本文讨论范畴。
- 用免费的Oracle VirtualBox 搭建虚拟机,安装Linux。
- 把宿主机的一个目录共享到虚拟机中,然后再用这个目录创建一个所有容器共享的数据卷容器。以此在宿主机和各容器之间实现数据共享和同步。
- 开发环境,版本管理环境,SDK环境,运行环境相互之间使用不同的容器将它们各自独立。并且这些容器可以快速的部署到测试甚至生产环境。
组件图
直接上图
- 图中只有一个角色,就是开发者了。
- 这个角色有两种主要用例,一是使用IDE等各种工具进行开发,二是使用特定端口访问运行环境的服务。在这里画的是80端口的web服务。
- 将Windows下目录C:\foo\bar\workspace共享到虚拟机内
- 虚拟机挂载该共享目录到/workspace
- 使用docker创建一个workspace数据卷容器,卷挂载的是虚拟机系统的/workspace
- 分别创建三个容器,包含运行环境,编译环境,版本管理环境。这三个容器都把workspace容器卷作为数据卷。
- 版本管理容器负责和远端代码仓库同步代码
- 编译环境负责把代码编译成可执行程序
- 运行环境负责运行可执行程序
- Docker会把运行环境的端口映射到虚拟机系统上
- 由于虚拟机配置成网桥模式,所以宿主机可以直接访问该端口
详细步骤
安装配置VirtualBox
安装VirtualBox:先到官网下载最新版本的VirtualBox安装包和扩展包,下载地址。这里需要注意的是,扩展包的版本必须和安装包的版本一致。
安装扩展包:双击安装Virtualbox。装好Virtualbox后,打开主界面,点击:菜单->管理->全局设定->扩展->添加。然后选中扩展包。
下载Alpine Linux安装盘: 下载地址。
这里多扯两句。Linux发行版多如牛毛,最流行的莫过于Ubuntu和CentOS。这两个发行版网上中英文资料,以及问题解答都很多,可能更适合初学者选择。我之所以选用AlpineLinux,是因为其轻量化的特点。有专门为虚拟机优化的版本,内核经过裁剪,安装盘只有几十MB。全部安装完不到300MB磁盘占用。启动也很快,占用资源少,在虚拟机中后台运行基本无感。我们基本上所有“功能”都基于Docker搭建,所以Linux宿主系统并不需要任何额外的功能,只要把Docker跑起来即可。另外,AlpineLinux是docker官方御用操作系统,很多镜像文件都基于Alpine构建。虽然宿主系统和镜像系统之间并无太多联系,但在学习曲线和操作习惯上,如果两者统一,效率会有有所提高。
另外提一下,目前Docker的宿主系统中,CoreOS也是一个很好的选择。CoreOS是专门为Docker量身打造的操作系统,拥有许多优秀特性。目前阿里云也支持该系统。有兴趣可以了解一下。
新建虚拟机:在VirtualBox新建一个虚拟机,类型选择“Linux 2.6 / 3.x / 4.x (64-bit)”。然后创建一个动态扩展的硬盘,大小可以选择100G,反正是动态扩展。创建完成后,手动修改如下选项:
- 使能PAE/NX
- 使能硬件加速
- 网卡配置成“桥接网卡”
- USB设置成3.0版本
- 共享文件夹添加一个工作目录:C:\foo\bar\workspace,名称设为“workspace”
- 光驱选择iso文件,然后找到我们刚才下载的AlpineLinux的iso
之所以选择基于“Linux 2.6 / 3.x / 4.x (64-bit)”进行配置,是因为VirtualBox没有默认的AlpineLinux的配置,而这个选项比较接近我们希望的配置。如果选择的是“Other Linux”,VirtualBox会给出一个老旧机型的配置。
网卡配置成“桥接网卡”是为了虚拟机和外面的物理网络能够相互访问。虚拟机能获得物理网络的IP。物理网络中的设备(包括Windows宿主机)可以像访问其他物理设备一样访问虚拟机(机器内部的Docker,后面会说)。
这里选择的共享文件夹,就是上面组件图中的共享文件夹,用于在宿主机,虚拟机和Docker之间双向共享数据用。
安装AlpineLinux
- 启动虚拟机,用光盘启动,然后用root登录AlpineLinux,没有密码
- 敲入命令
setup-alpine
- 跟随命令行向导一步一步执行
- 在选择安装源时,敲入“f”让系统自己寻找一个最快的源
- 在选择安装目标盘时,敲入“sda”
- 在选择分区类型时,敲入“sys”
- 几分钟就装完了,退出光盘,重启虚拟机
配置AlpineLinux
把网络配置改成DHCP(非必须): 编辑/etc/network/interfaces,删除eth0的固定IP配置,然后添加一行:
iface eth0 inet dhcp
配置打开软件仓库:编辑/etc/apk/repositories,把community源前面的注释去掉。docker包是在community仓库里面的,所以这步必须做。
升级系统:执行如下命令
apk update
apk upgrade
配置sshd:AlpineLinux默认是关闭root用户ssh登录的。最安全的做法是创建一个非root用户。懒人可以直接打开root登录。编辑/etc/ssh/sshd_config,加入一行:
PermitRootLogin yes
安装Virtualbox扩展包:这步是为了使AlpineLinux能够支持VirtualBox的共享目录。命令:
apk add virtualbox-guest-additions virtualbox-guest-modules-virt virtualbox-guest-modules-vanilla
启动挂载共享目录:这步是为了让AlpineLinux每次启动都自动挂载VirtualBox的共享目录。编辑/etc/fstab,加入一行
workspace /workspace vboxsf defaults 0 0
安装Docker:如下命令安装docker,并使其服务端随系统启动:
apk add docker
ln -s /etc/init.d/docker /etc/runlevels/default/docker
Docker国内镜像加速:创建一个文件/etc/docker/daemon.json,内容为(如果有阿里云或者DaoCloud账号,也可以配置成私有的加速链接):
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
搞定,重启!
创建容器
下载镜像
首先下载一个Alpine的基础镜像。行文时Alpine最新版本为3.8,建议按版本装镜像而不是拉latest,这样以后好进行版本管理和迁移。
docker pull alpine:3.8
之所以再次选择AlpineLinux作为容器的基础镜像,还是因为其轻量化。Alpine3.8的基础镜像仅为5MB!
创建数据卷容器
docker run -it --name=workspace -v /workspace:/workspace:Z alpine:3.8
这个命令的意思是创建一个名字为workspace的容器,作为卷挂载本地(虚拟机Linux)的目录/workspace到容器的/workspace目录,使用alpine作为镜像,镜像版本3.8。那个大写的Z在这里没什么作用。但是如果虚拟机宿主Linux是CentOS这类启用了SELinux的系统,必须加这个参数,要不然容器是无法读写这个目录的!
安装SDK镜像
这里以Golang为例,dockerhub上直接有官方的golang镜像,所以直接pull然后run即可
docker pull golang:1.10.3-alpine3.8
docker run -it --rm --volumes-from workspace golang:1.10.3-alpine3.8 [your commands...]
创建SCM镜像
这里以git为例。git是个很轻量级的工具,我们可以自己制作一个镜像。
首先编写Dockerfile,这个文件的作用就跟C/C++的Makefile差不多。类容如下:
FROM alpine:3.8
MAINTAINER Cary Tan tx-cary@163.com
ENV PS1='[git@docker $PWD]\$ '
RUN echo "http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.8/main" > /etc/apk/repositories \
&& echo "http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.8/community" >> /etc/apk/repositories \
&& apk update \
&& apk add git git-doc \
&& mkdir -p /workspace
WORKDIR /workspace
CMD /bin/sh
然后执行如下命令构建并上传镜像到你的dockerhub。
docker build -t <your_name>/git:<tag> /path/to/your/Dockerfile/
docker push <your_name>/git:<tag>
运行SCM镜像去下载某项目的代码:
docker run -it --rm --volumes-from workspace <your_name>/git:<tag> git clone ...
创建RUN镜像
RUN镜像的环境根据项目不同,区别会很大。有时候可能还需要很多images/container一起协同工作。我在这里就举一个最简单的,用golang做的一个http hello world。
- 首先,在windows机器的workspace目录下创建一个叫gohttp的子目录,然后创建一个原文件main.go。源码如下:
package main
import (
"fmt"
"net/http"
)
const (
host string = ":80"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello world!")
})
fmt.Println("Listening ", host)
http.ListenAndServe(host, nil)
}
- 然后用golang的SDK镜像进行编译:
docker run -it --rm --volumes-from workspace golang:1.10.3-alpine3.8 go build -o /workspace/bin/gohttp /workspace/gohtt
p/main.go
- 用alpine镜像创建一个运行容器。也即我们使用原始alpine镜像作为我们的RUN镜像:
docker run -it -p 80:80 --volumes-from workspace alpine:3.8 /workspace/bin/gohttp
以上关键参数是“-p 80:80”。意思是将容器内的80端口映射到宿主机(虚拟机中的Linux系统)上。
此时,顺利的话,我们可以看到终端打印
Listening :80
- 最后,只需打开Windows上的浏览器,输入虚拟机IP地址,就可以验证我们的Hello World了!
有疑问加站长微信联系(非本文作者)