Que's Blog

WebFrontEnd Development

0%

Docker构建前端开发环境

写在前面

本文写作前面其实个人也尝试用过docker,那时候docker for Mac尚需要virtualBox支持。不过到本文开篇时候,docker的版本已经是 1.12.0-rc3 ,似乎已经不需要单独的virtualBox了。

之前因为virtualBox依赖所以个人放弃了继续玩下去,一来因为对性能有点洁癖,二来因为vagrant还是能满足需求的同时似乎没比docker在mac上性能差。不过现在看到不需要virtualBox了,所以又开始折腾一下,算玩物丧志吧。

安装

安装这事,感觉还是不提了,这个直接上官网链接就好了,这里是 直达飞机

获取镜像

获取镜像使用 docker pull 命令即可。
如果顾虑dockerHub的网速,可以使用网易的仓库。网易蜂巢.
这里安装的是ubuntu:

1
2
3
4
# 网易
docker pull hub.c.163.com/public/ubuntu:14.04-common
# dockerHub
docker pull ubuntu

PS:顺便说一句,直接使用dockerHub现在速度也非常理想,为了更好的兼容性,建议还是dockerHub吧。本文最初使用网易的镜像,但是运行run失败了,始终无法进入tty,数次ctrl-c重新运行也无效。而dockerHub的几乎不到一秒就进入了tty。
对比了一下网易和dockerHub的镜像,网易的达到350多M,而dockerHub的只有182M,这应该是Mac新版本的docker导致的。

运行容器

运行容器需要运行 docker run 命令。run命令参数比较多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
--add-host          -- Add a custom host-to-IP mapping
--attach -a -- Attach to STDIN, STDOUT or STDERR
--cap-add -- Add Linux capabilities
--cap-drop -- Drop Linux capabilities
--cidfile -- Write the container ID to the file
--cpu-shares -c -- CPU shares (relative weight)
--cpuset-cpus -- CPUs in which to allow execution (0-3, 0,1)
--detach -d -- Run container in the background, print new container id
--device -- Add a host device to the container
--dns -- Set custom dns servers
--dns-search -- Set custom DNS search domains
--entrypoint -- Overwrite the default entrypoint of the image
--env -e -- Set environment variables
--env-file -- Read in a file of environment variables
--expose -- Expose a port from the container without publishing it to your host
--hostname -h -- Container host name
--interactive -i -- Keep STDIN open even if not attached
--ipc -- IPC namespace to use
--label -l -- Set meta data on a container
--link -- Add link to another container (name:alias)
--log-driver -- Logging driver for the container
--lxc-conf -- Add custom LXC options
--mac-address -- Container MAC address (e.g. 92:d0:c6:0a:29:33)
--memory -m -- Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
--name -- Assign a name to the container
--net -- Set the Network mode for the container
--pid -- PID namespace to use
--privileged -- Give extended privileges to this container
--publish -p -- Publish a containers port to the host (format: ip:hostPort:containerPort/protocol)
--publish-all -P -- Publish all exposed ports to the host interfaces
--restart -- Restart policy to apply when a container exits
--rm -- Automatically remove the container when it exits (incompatible with -d)
--security-opt -- Security Options
--sig-proxy -- Proxify all received signal to the process (even in non-tty mode)
--tty -t -- Allocate a pseudo-tty
--ulimit -- Ulimit options
--user -u -- Username or UID
--volume -v -- Bind mount a volume (e.g. -v /host:/container or -v /container)
--volumes-from -- Mount volumes from the specified container(s)
--workdir -w -- Working directory inside the container

我们这里运行:

1
docker run -t -i ubuntu /bin/bash

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。这个在上面的选项里面有。

终止&运行容器

终止和重新运行一个容器都很容易。

1
2
3
4
5
➜  ~ docker ps #获取容器的列表
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c72f5174d56a ubuntu "/bin/bash" 3 minutes ago Up 3 minutes naughty_almeida
➜ ~ docker stop c72f5174d56a # 将id填入即可终止
➜ ~ docker start c72f5174d56a # 将id填入即可运行

exce

Docker的优势是轻量级,一个高配置服务器可以同时运行超过上百个Container,这就决定了绝大多数的Container都是在后台静默运行的。
之前说的终止和运行容器,其实就是说,在后台进行终止和运行。但是容器最终还是需要进行shell交互的。这里就需要用到exce命令。

1
docker exec -it baseBox zsh

这里的-i和-t和run是一样的,baseBox是容器的名称,zsh是要运行的命令。
几乎是一秒后,容器就进入了tty交互了。此时我们就可以开始进入自己的镜像定制了。

定制镜像(vim-wrapper)

这里就随意记录一下自己的折腾记录。记录是按标题顺序来的。

vim

先安装vim非我所愿。只是ubuntu这个最小的镜像没有vi也没有vim导致我没法换源。。。跨洋的网速大家都知道的。

1
2
3
apt-get update
apt-get install vim
vim /etc/apt/sources.list

然后填入163的源。

1
2
3
4
5
6
7
8
9
10
11
#163
deb http://cn.archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-backports main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-proposed main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-proposed main restricted universe multiverse

然后 apt-get update更新之。

oh-my-zsh && git

oh-my-zsh是愉快工作的第一步。感觉这是个没有就没法活的东西。
因为安装需要git和zsh和wget依赖,先行安装一下。考虑到git这东西其实是必备的,所以一起弄了。

1
2
apt-get instal -y git zsh wget
sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"

vim plugins + YouCompeleteMe

YouCompeleteMe其实也是vim plugins。。。这个大家都知道,不过这东西好用到可以拿出来着重说了。基于语意的补全加上ternJs的加持,很大程度上可以媲美IDE补全了。如果用vim做开发,这个插件是体验的最大保证之一了。个人因为是前端开发,所以这里的配置基本以此为准,只处理js补全。其他语言的可以酌情看github的Readme处理。

ps: https://github.com/que01/vimrc 这个仓库是根据 https://github.com/amix/vimrc 项目而来。6k的star看着比较眼馋。主要补充了YouCompeleteMe和一个巴掌不到的插件补充。可以放心用。

1
2
3
git clone https://github.com/que01/vimrc ~/.vim_runtime
cd ~/.vim_runtime && git submodule init && git submodule update
cd ~/.vim_runtime/sources_non_forked/YouCompleteMe && git submodule update --init --recursive

到这里配置就完事了。不过还需要对YouCompeleteMe单独编译一番。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 先处理编译环境
apt-get install build-essential cmake && apt-get install python-dev
# 会被安装的包,记录一下,等会编译完了删掉,删掉。。。不然这image打包起来也太大了。
# binutils build-essential bzip2 cmake cmake-data cpp cpp-5 dpkg-dev fakeroot g++ g++-5 gcc gcc-5 libalgorithm-diff-perl libalgorithm-diff-xs-perl libalgorithm-merge-perl
libarchive13 libasan2 libatomic1 libcc1-0 libcilkrts5 libcurl3 libdpkg-perl libfakeroot libfile-fcntllock-perl libgcc-5-dev libgomp1 libisl15 libitm1 libjsoncpp1 liblsan0
liblzo2-2 libmpc3 libmpfr4 libmpx0 libquadmath0 libstdc++-5-dev libtsan0 libubsan0 libxml2 sgml-base xml-core
# node安装 ternJS需要
apt-get install nodejs curl && mv /usr/bin/nodejs /usr/bin/node #其他发行版都是node,这里改改不然麻烦
# npm安装
curl -L https://www.npmjs.com/install.sh | sh
# 编译开始
cd ~/.vim/bundle/YouCompleteMe && ./install.py --tern-completer
# 完成后删除工具包 wget curl 还有build-essential cmake和相关依赖
apt-get remove curl wget binutils build-essential bzip2 cmake cmake-data cpp cpp-5 dpkg-dev fakeroot g++ g++-5 gcc gcc-5 libalgorithm-diff-perl libalgorithm-diff-xs-perl libalgorithm-merge-perl
libarchive13 libasan2 libatomic1 libcc1-0 libcilkrts5 libcurl3 libdpkg-perl libfakeroot libfile-fcntllock-perl libgcc-5-dev libgomp1 libisl15 libitm1 libjsoncpp1 liblsan0
liblzo2-2 libmpc3 libmpfr4 libmpx0 libquadmath0 libstdc++-5-dev libtsan0 libubsan0 libxml2 sgml-base xml-core

到这里基本就完事了,不过下一步我们继续来精简体积保证images更小。这里从更多还是YouCompeleteMe下手,毕竟这玩意儿实在太大了。

这里操作依赖zsh,所以务必运行一下zsh进入oh-my-zsh。其实主要就一条:

1
2
3
4
5
6
#删除版本库
cd ~/.vim_runtime
rm -rf */**/.git<TAB> #然后oh-my-zsh会将所有的.git自动补全处理,回车,删除,这些git版本记录是体积大头
#删除不要的third_party,我们只需要前端补全
cd ~/.vim_runtime/sources_non_forked/YouCompleteMe/third_party/ycmd/third_party
rm -rf gocode godef JediHTTP

然后我查看了一下体积:

1
2
3
4
5
6
7
8
9
10
11
➜  third_party du -h --max-depth=1 .
3.5M ./requests
216K ./racerd
11M ./tern_runtime
28M ./OmniSharpServer
4.9M ./python-future
472K ./argparse
668K ./waitress
1.3M ./bottle
48K ./frozendict
49M .

似乎也没可以放心删了,其他的不知道会不会有依赖。。。不删了。最后看了下体积,整个root目录当前118M,顺手把.vim_runtime下么的doc目录全删了(rm -rf */**/doc),反正我用不上。。。现在116M。。。

打包前的清场

1
apt-get clean && apt-get autoremove && apt-get autoclean && rm -rf /tmp

打包

1
docker commit -a que01  -m 'vim config & zhs config & git' baseBox que01/vim

最后查看打包好的镜像,体积:561.6 MB。
。。。有些心醉啊。。。。git,oh-my-zsh,vim这3个作为工作基础那真是一个都不能少,不过vim+youcomplete确实占用了太多空间了,youcomplete单独的占用达到105M+,vim+python目测有80到100M,git有60,oh-my-zsh暂用不大大概有7M的样子,加起来大概有270M的样子(加上ubuntu有125,至少也会有将近400了),加上杂七杂八的依赖,估计也小不下去了。

发布

1
docker push que01/vim

结语

这篇呢,记录了自己打包的基础docker image,总体来讲还是可以用了,不过容器之间的连接还是有些犯晕,有时间我再另行整理记录一下。