使用docker高效搭建開發環境
同時,我也是有一些相對的小潔癖,很喜歡linux中權限最小化原則,我也不喜歡自己的環境中有太多不知道的東西。
做了多年的web開發,我接觸到的環境大致如下:
操作系統從centos5到centos7;
webserver從apache到nginx;
開發語言從最初的php5.2到php7,又到現在主要使用Go,馬上還會開始接觸C++;
數據庫從mysql5.1到現在的5.7,前陣子又開始折騰mariadb;
cache選型從memcache到redis;
隊列用過kafka,去年開始大量使用nsq;
公司雖然有專門負責部署、運維這些服務的同學,但我在開發的時候,還是喜歡自己來搭建這些東西,因為這樣通常可以對使用到的服務有更多的認識,也能幫助自己使用的更好。
今天我就來和大家分享下我是如何高效的搭建好自己的開發環境的。
搭建前說明
這裏先說明一點,對每個開源軟件,我幾乎都是自己編譯部署的,而不會使用類似yum install
這種方式,也很少直接下載官方編譯好的二進制包,這都是為了能多深入了解用到的開源軟件。
但一些依賴的動態庫文件,如zlib等,還有編譯工具,如gcc、make等,我都是通過方便的yum install
這種方式直接安裝的,否則會累死。
傳統做法
我在很長的一段時間內,都是把每個軟件的編譯、安裝過程寫成一個腳本,之後再需要用的時候直接運行腳本即可,但這樣的方式,通常會遇到下面這些問題:
腳本只能在我當時的操作系統環境下運行。記得當時購買過不同服務商的vps,雖然不同vps我都使用同樣的linux發行版,但腳本通常都不能一鍵跑完。這也是沒辦法,因為每個vps服務商都會制作自己的操作系統鏡像版本。
操作系統升級,如centos5 - 6,或是換為ubuntu,這樣基本上腳本都跑不了。
軟件升級,如mysql5.2 - 5.6,構建工具改為cmake,依賴庫改變或升級。
如果某個軟件依賴的公共庫版本和其它軟件不同,且公共庫升級後和舊版不兼容,那你就只能為這個軟件單獨編譯公共庫了,如果只是普通的公共庫還好,但如果是所需要的編譯工具版本不同,那可就慘了。
上面這些問題,如果你想每個發行版維護一個腳本,那會累死,因為一旦你每次想升級一個軟件,難道每個發行版都要編譯一遍嗎?這就變成了收獲價值很低的體力勞動了。
由於喜歡折騰的個性,我對操作系統的升級以及軟件包版本的升級又經常發生,所以一直以來,我都在尋找一個好方法,能很方便的維護好自己的開發環境,盡量做到只=新東西只為它工作一次,最後我找到了docker,目前我都是用它來搭建自己的開發環境的。
docker做法
先概括介紹下我的方法:
讓每個軟件運行在容器中,因為運行的容器環境是可以固定下來的,所以編譯安裝腳本寫一個就可以了。
代碼使用數據卷的方式加載到需要的容器中。
因為是開發環境,所以網絡方面使用最簡單的
--net=host
。將鏡像的創建、容器的啟動維護在git項目中,並抽象出統一的構建過程,很方面的做到新軟件接入,新機器部署。
下面用實例來說明把:
示例Nginx環境構建
我將構建過程放到git中:https://gitee.com/andals/docker-nginx
Readme中記錄了構建所需要執行的腳本命令,大家訪問上面的網址就可以看到,這裏我簡單介紹下項目的結構:
├── Dockerfile //創建鏡像的Dockerfile├── pkg //編譯好的二進制包,可以直接使用,此外軟件運行的一些配置文件或第三方包也放在這裏│ ├── conf │ │ ├── fastcgi.conf │ │ ├── http.d │ │ ├── include│ │ ├── koi-utf │ │ ├── koi-win │ │ ├── logrotate.conf │ │ ├── logrotate.d │ │ ├── mime.types │ │ ├── nginx.conf │ │ ├── scgi_params │ │ ├── uwsgi_params │ │ └── win-utf │ ├── luajit-2.0.3.tar.gz │ └── nginx-1.8.1.tar.gz ├── README.md ├── script //裏面放構建腳本│ ├── build_image.sh //構建鏡像使用│ ├── build_pkg.sh //編譯軟件包時使用│ ├── init.sh //容器啟動時執行│ └── pre_build.sh //軟件依賴的共享庫,編譯和構建時都會用到└── src //編譯時需要的軟件源碼 ├── modules │ ├── ngx_devel_kit-0.2.19.tgz │ ├── ngx_echo-0.53.tgz │ └── ngx_lua-0.9.7.tgz ├── nginx-1.8.1.tar.gz └── openssl-1.0.2h.tar.gz
DockerFile說明
Dockerfile結構如下:
FROM andals/centos:7MAINTAINER ligang <[email protected]> LABEL name="Nginx Image"LABEL vendor="Andals"COPY pkg/ /build/pkg/ COPY script/ /build/script/ RUN /build/script/build_image.sh CMD /build/script/init.sh
整個構建框架為:
把構建需要的包(pkg目錄中)放到鏡像中
把構建腳本放到鏡像中
執行構建腳本
容器啟動時,執行init.sh,裏面啟動相應的服務
Readme.md中記錄了執行構建的命令和容器運行命令,示例運行如下:
ligang@vm-xubuntu16 ~/devspace/dbuild $ git clone [email protected]:andals/docker-nginx.git nginx Cloning into 'nginx'... ...... ligang@vm-xubuntu16 ~/devspace/dbuild $ cd nginx/ ligang@vm-xubuntu16 ~/devspace/dbuild/nginx $ ngxVer=1.8.1ligang@vm-xubuntu16 ~/devspace/dbuild/nginx $ docker build -t andals/nginx:${ngxVer} ./ ligang@vm-xubuntu16 ~/devspace/dbuild/nginx $ docker build -t andals/nginx:${ngxVer} ./ Sending build context to Docker daemon 30.7MB Step 1/8 : FROM andals/centos:7...... Successfully built ea8147743031 Successfully tagged andals/nginx:1.8.1ligang@vm-xubuntu16 ~/devspace/dbuild/nginx $ docker run -d --name=nginx-${ngxVer} --volumes-from=data-home -v /data/nginx:/data/nginx --net=host andals/nginx:${ngxVer} dbf3c0617eb34c4b1b4ea54c2961989612d5474db3b1acd1d717221e6e5cb516
說明:
--volumes-from=data-home
這個就是我放置代碼的數據卷,我喜歡把代碼放到$HOME
下面,所以這個卷的相關信息如下:
ligang@vm-xubuntu16 ~ $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES578912a08ea7 andals/centos:7 "echo Data Volumn Ho…" 9 days ago Exited (0) 9 days ago data-home ...... ligang@vm-xubuntu16 ~ $ docker inspect 578912a08ea7 ...... "Mounts": [ { "Type": "bind", "Source": "/home", "Destination": "/home", "Mode": "", "RW": true, "Propagation": "rprivate" } ......
/data/nginx
中放置nginx的conf、log等,每個軟件運行時的conf、log、data等我都統一放置在/data下面,如:
ligang@vm-xubuntu16 ~ $ tree -d /data/ -L 1/data/ ├── mariadb ├── nginx └── redis ligang@vm-xubuntu16 ~ $ tree -d /data/nginx/ /data/nginx/ ├── conf │ ├── http.d │ ├── include │ └── logrotate.d └── logs
啟動容器時使用
--net=host
,作為開發環境簡單實用
我就是通過這種方法完成了開發環境的構建,不再有多余的重復工作,並且新機器部署開發環境效率極高。
我目前用到的容器環境如下:
ligang@vm-xubuntu16 ~ $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dbf3c0617eb3 andals/nginx:1.8.1 "/bin/sh -c /build/s…" 33 minutes ago Up 33 minutes nginx-1.8.13e31ef433298 andals/php:7.1.9 "/bin/sh -c /build/s…" 8 hours ago Up 8 hours php-7.1.9360f94bf9c43 andals/jekyll:latest "/bin/sh -c /build/s…" 9 days ago Up 10 hours jekyll-latest 0a7d58d1ca5e andals/redis:4.0.8 "/bin/sh -c /build/s…" 9 days ago Up 10 hours redis-4.0.8fdaa655b4a11 andals/samba:4.4.16 "/bin/sh -c /build/s…" 9 days ago Up 10 hours samba-4.4.166ad00a69befd andals/mariadb:10.2.14 "/bin/sh -c /build/s…" 9 days ago Up 10 hours mariadb-10.2.14578912a08ea7 andals/centos:7 "echo Data Volumn Ho…" 9 days ago Exited (0) 9 days ago data-home
輔助工具dockerbox
使用docker環境後,有個問題,就是沒有辦法很方便的和軟件交互。
這是因為軟件都執行在容器中,比如重啟nginx吧,需要下面這幾步:
找到nginx這個容器
進入nginx這個容器
在容器裏面再執行reload
也可以是:
找到nginx這個容器
使用
docker exec
但無論哪種方式,都比原先直接執行命令麻煩的多。
另外,有時也需要進入容器中,查看服務的運行情況。
為了方便的做這些事情,我開發了一個工具dockerbox
,可以很方便的做到這些事情。
dockerbox的詳情及使用方法請見:https://github.com/ligang1109/dockerbox
配置開機運行
最後再說下如何配置開機啟動。
我使用虛擬機搭建的開發環境,所以配置這個會省事好多,我使用用了systemd
:
ligang@vm-xubuntu16 ~ $ ll /lib/systemd/system/dstart.service -rw-r--r-- 1 root root 229 4月 3 21:35 /lib/systemd/system/dstart.service ligang@vm-xubuntu16 ~ $ cat /lib/systemd/system/dstart.service [Unit] Description=Docker Container Starter After=network.target docker.service Requires=docker.service [Service] ExecStart=/usr/local/bin/dbox -dconfPath=/home/ligang/.dconf.json start all [Install] WantedBy=multi-user.target
dbox請參考dockerbox的使用方法。
結束語
上面說的是我現在使用的開發環境搭建方法,有興趣愛折騰的同學不妨試試看,如果你有更好的方法,也希望能分享給我。
生命不息,折騰不止。
使用docker高效搭建開發環境