MAC+VirtualBox+Docker搭建私有CentOS Docker容器
Docker源意码头工人, 是一款更轻量级的虚拟化快速部署工具, 他的优点在于非常傻瓜的配置和管理, CPU/内存的低消耗, 快速开/关机, 可以非常方便的运行和释放容器, 便捷的连接宿主机器和容器以及0成本的commit and export到其他任意环境, 绝对是一款优于vagrant等同类型工具的SA居家旅行必备利器.
通俗来讲Docker其实就是将我们在虚拟机上定制的系统打包成一个私有Docker镜像, 然后通过将镜像传到本地, Docker本身会利用Vbox创建一个虚拟机实例(该虚拟机内核支持Docker)并与本地共享同一个目录结构, 最终利用docker内嵌命令使用我们的私有镜像创建若干子虚拟容器, 从而实现本地虚拟化部署.
Windows与MAC平台内核因为本身与Docker不兼容, 所以我们需要本地安装VirtualBox+Docker, 利用VirtualBox创建一个可兼容Docker的虚拟机.
Linux内核为3.0以上的发行版可直接安装Docker, 无需安装第三方虚拟机.
本文将详细介绍如何创建一个私有的Docker容器, 并部署到不同环境中, 使用Windows和Linux平台的同学也可以参考本文的配置.
话说一开始我是拒绝的, 但经过2天的研究和部署, 我震精了, 这他喵的绝对是神一般的利器, 他的跨平台的一些功能(有待挖掘)绝对可以成为未来的运维部署发展趋势.
还有不得不佩服老外的想象力, 这官网的动物代表不同的系统环境, Docker代表码头工人的对不同系统环境的迁移整合, 整得都成动物世界了, 不过还真别说, 这码头工人的效率还真不错, 动物园的动物们对这小哥的发货效率绝对满意, 必须五星好评.
不说了, 我去啃老外的面包去了....
官方文档: https://docs.docker.com
一.环境部署
Local system: MAC OS X 10.10.5
VirtualBox: VirtualBox-5.0.10
docker: DockerToolbox-1.9.1b.pkg
Docker sample system: CentOS6.7 x64 minimal (docker.example.com)
本机需安装Virtualbox和Docker, 安装过程(略).
TIP:本机为ubuntu或其他Linux发行版内核为3.0以上的朋友因为本地系统内嵌KVM, Docker可直接调用KVM, 所以无需安装Virtualbox作为Docker虚拟机.
二.Docker配置
1.在Virtualbox新建虚拟机并安装CentOS 6.7 x64(略)
2.安装openssh-clients (VirtualBox虚拟机)
# yum install openssh-clients -y
3.登陆CentOS虚拟机并安装EPEL YUM源 (VirtualBox虚拟机)
# rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
4.配置yum (VirtualBox虚拟机)
# cd /etc/yum.repos.d
# vi CentOS-Base.repo
找到[centosplus]源下,修改enabled=1
# sed -i 's/$releasever/6/g' CentOS-Base.repo
# sed -i 's/$basearch/x86_64/g' CentOS-Base.repo
5.安装Docker并开启Docker服务 (VirtualBox虚拟机)
# yum install docker-io -y
# service docker start
6.部署docker
1).常用命令(本地)
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
可以看到目前docker并未有可用的镜像.
2).配置脚本目录 (VirtualBox虚拟机)
# cd /root
# mkdir scripts
# cd scripts
# vi createimg.sh
源自:https://github.com/docker/docker/blob/master/contrib/mkimage-yum.sh
#!/usr/bin/env bash # # Create a base CentOS Docker image. # # This script is useful on systems with yum installed (e.g., building # a CentOS image on CentOS). See contrib/mkimage-rinse.sh for a way # to build CentOS images on other systems. usage() { cat <<EOOPTS $(basename $0) [OPTIONS] <name> OPTIONS: -y <yumconf> The path to the yum config to install packages from. The default is /etc/yum.conf for Centos/RHEL and /etc/dnf/dnf.conf for Fedora EOOPTS exit 1 } # option defaults yum_config=/etc/yum.conf if [ -f /etc/dnf/dnf.conf ] && command -v dnf &> /dev/null; then yum_config=/etc/dnf/dnf.conf alias yum=dnf fi while getopts ":y:h" opt; do case $opt in y) yum_config=$OPTARG ;; h) usage ;; \?) echo "Invalid option: -$OPTARG" usage ;; esac done shift $((OPTIND - 1)) name=$1 if [[ -z $name ]]; then usage fi target=$(mktemp -d --tmpdir $(basename $0).XXXXXX) set -x mkdir -m 755 "$target"/dev mknod -m 600 "$target"/dev/console c 5 1 mknod -m 600 "$target"/dev/initctl p mknod -m 666 "$target"/dev/full c 1 7 mknod -m 666 "$target"/dev/null c 1 3 mknod -m 666 "$target"/dev/ptmx c 5 2 mknod -m 666 "$target"/dev/random c 1 8 mknod -m 666 "$target"/dev/tty c 5 0 mknod -m 666 "$target"/dev/tty0 c 4 0 mknod -m 666 "$target"/dev/urandom c 1 9 mknod -m 666 "$target"/dev/zero c 1 5 # amazon linux yum will fail without vars set if [ -d /etc/yum/vars ]; then mkdir -p -m 755 "$target"/etc/yum cp -a /etc/yum/vars "$target"/etc/yum/ fi yum -c "$yum_config" --installroot="$target" --releasever=/ --setopt=tsflags=nodocs \ --setopt=group_package_types=mandatory -y groupinstall Core yum -c "$yum_config" --installroot="$target" -y clean all cat > "$target"/etc/sysconfig/network <<EOF NETWORKING=yes HOSTNAME=localhost.localdomain EOF # effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb --keep-services "$target". # locales rm -rf "$target"/usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive} # docs and man pages rm -rf "$target"/usr/share/{man,doc,info,gnome/help} # cracklib rm -rf "$target"/usr/share/cracklib # i18n rm -rf "$target"/usr/share/i18n # yum cache rm -rf "$target"/var/cache/yum mkdir -p --mode=0755 "$target"/var/cache/yum # sln rm -rf "$target"/sbin/sln # ldconfig rm -rf "$target"/etc/ld.so.cache "$target"/var/cache/ldconfig mkdir -p --mode=0755 "$target"/var/cache/ldconfig version= for file in "$target"/etc/{redhat,system}-release do if [ -r "$file" ]; then version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$file")" break fi done if [ -z "$version" ]; then echo >&2 "warning: cannot autodetect OS version, using '$name' as tag" version=$name fi tar --numeric-owner -c -C "$target" . | docker import - $name:$version docker run -i -t $name:$version echo success rm -rf "$target"
# chmod +x createimg.sh
3).执行脚本(VirtualBox虚拟机)
该脚本将自动生成docker镜像,centos6.7base为镜像名,脚本执行log保存在/tmp下
# ./createimg.sh centos6.7base
4).查看生成的docker镜像(VirtualBox虚拟机)
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos6.7base 6.7 8355bdcdcde0 5 minutes ago 166.3 MB
5).查看生成的docker容器(VirtualBox虚拟机)
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a7392bd18606 centos6.7base:6.7 "echo success" About a minute ago Exited (0) About a minute ago sleepy_swartz
6).删除容器(VirtualBox虚拟机)
# docker rm a7392bd18606
7).导出Docker缓存镜像到磁盘(VirtualBox虚拟机)
# docker save 8355bdcdcde0 > /tmp/centos67base.tar
# ll /tmp
-rw-r--r-- 1 root root 174095360 Dec 14 04:23 centos67base.tar -rw-r--r-- 1 root root 20284 Oct 16 03:21 vboxguest-Module.symvers -rw-------. 1 root root 0 Oct 16 00:21 yum.log
8).拷贝该镜像到本地(本地)
Tip: 因为笔者是MAC系统, 所以系统自带CLI scp命令, windows用户可以使用winscp进行ssh传输.
# cd ~/Work/Docker/centos67base
# scp root@docker.example.com:/tmp/centos67base.tar .
7.使用MAC下的docker加载该镜像
在MAC Application/Docker目录下运行Docker Quickstart Terminal, 会弹出Terminal并运行docker的相应初始化配置, 完成后会进入CLI界面.
TIP: 这里实际上是Docker调用了本地VirtualBox, 并在其创建了一个新的Docker虚拟机instance(可以通过打开VBOX GUI查看), 所以我们实际上已经进入了这个instance CLI界面, 但笔者惊奇的发现这里的目录结构和MAC本地是一致的, 可以得出来的结论是Docker和MAC共同share一个CLI. 所以大家可能会有错觉自己还在本地, 实际情况是我们已经登录到Docker母虚拟机, 随后我们会在这个母机上创建多个容器(子虚拟机).
Windows平台下该执行文件可在开始菜单获取
1).加载刚才制作的镜像(Docker CLI)
# docker load < centos67base.tar
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE <none> <none> 8355bdcdcde0 21 hours ago 166.3 MB
2).添加Repository和tag(Docker CLI)
默认这两项都为空,所以可以根据IMAGE ID去给这个image添加相关的标注
# docker tag 8355bdcdcde0 centos67base:6.7
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos67base 6.7 8355bdcdcde0 21 hours ago 166.3 MB
3).创建docker容器并加载该镜像(Docker CLI)
# docker run -i -t --privileged 8355bdcdcde0 /bin/bash
Tip: -i和-t配合使用实际上是为了保持一个TTY标准输入处于一个LISTEN状态无论是否被连接, 简单来说就是创建一个CLI终端连接.
--privileged用来开启扩展权限给容器, 主要开启容器iptables等类似内核集成模块, 默认无此参数使用iptables会报错.
8355bdcdcde0为IMAGE ID, /bin/bash表示使用bash shell格式进入终端.
已成功登录子虚拟容器
# uname -a
Linux 33e93edb56e3 4.1.13-boot2docker #1 SMP Fri Nov 20 19:05:50 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
# cat /etc/system-release
CentOS release 6.7 (Final)
可以看到已经登陆到子虚拟容器, 至此我们就成功制作并加载了我们私有的docker镜像.
4).关闭子虚拟容器
# exit
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33e93edb56e3 8355bdcdcde0 "/bin/bash" 16 minutes ago Exited (0) 8 seconds ago awesome_joliot
这里可以看到这个容器在16分钟前生成, 现已经关闭了8秒钟, 简单来说这里去退出Docker容器可以理解为虚拟机的保存状态(挂起), 在我们创建这个容器后的所有的change都不会丢失, 除非你删除这个容器, 但之前开启的服务socket会丢失, 所以重新打开这个容器需重启该服务.
5).重新开启并连接该容器(Docker CLI)
# docker start 33e93edb56e3
# docker attach 33e93edb56e3
Tip: 这里执行attach后不会立即进入容器命令行,需要再回车才能显示.
6).断开该容器(Docker CLI)
这里可以在CLI下利用快捷键ctrl+P, 然后Ctrl+Q去快速切回本地环境.
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33e93edb56e3 8355bdcdcde0 "/bin/bash" 33 minutes ago Up 7 minutes awesome_joliot
这里可以看到容器并没有退出, 其状态显示为已运行7分钟.
8).重命名该容器(Docker CLI)
# docker rename awesome_joliot centos6.7_base
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33e93edb56e3 8355bdcdcde0 "/bin/bash" 35 minutes ago Up 9 minutes centos6.7_base
9).重新连接该容器(Docker CLI)
# docker attach 33e93edb56e3
这里也可以直接使用刚才修改后的容器名登陆
# docker attach centos6.7_base
所以实际上整个Docker的运行步骤:
加载tar包到docker缓存镜像 => 利用该镜像创建容器 => 启动容器 => 连接容器
8).删除并重新创建容器(Docker CLI)
# docker rm 33e93edb56e3
# docker run -i -t --privileged 8355bdcdcde0 /bin/bash
# exit
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 861dd99563ef 8355bdcdcde0 "/bin/bash" 18 seconds ago Exited (0) 2 seconds ago gloomy_lovelace
我们可以看到容器ID变成861dd99563ef, 之前的容器和容器的所有change已经彻底删除.
到这里我们介绍了Docker的使用, 基本上公司sa会将一份基本的Docker系统镜像分发给每位开发人员, 开发人员会利用这个统一的系统环境去coding, 如果在开发过程中遇到了一个BUG或者问题无法修复, 如何将当前的状态备份并分发给其他同事去处理? 这里就可以利用Dock commit去保存我们的系统状态到镜像中, 利用之前的命令export and transfer该镜像给相应Programmer进行进一步的处理, 当然我们也可以利用该功能去保存不同开发产品的版本状态.
8.Commit Docker镜像(Docker CLI)
我们可以安装一个apache并commit镜像来模拟上述情况.
# docker start 861dd99563ef
e8b478f41b5c
# docker attach 861dd99563ef
# exit
# docker commit 861dd99563ef centos67base/apache:apache_base
d9aef85ea282439634c36b3e8d51356396bb116c6ea51f11f8b128bf1fdb89a6
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos67base/apache apache_base d9aef85ea282 7 seconds ago 233.6 MB centos67base 6.7 8355bdcdcde0 23 hours ago 166.3 MB
我们可以看到我们生成了一个基于centos67base的apache版本镜像
9.导出该新版本镜像(Docker CLI)
# docker save centos67base/apache > centos67_apache.tar
# ll
-rw-r--r-- 1 XXX staff 242053632 Dec 15 16:34 centos67_apache.tar -rw-r--r-- 1 XXX staff 174095360 Dec 15 13:26 centos67base.tar
TIP: 由于Docker虚拟机与本地MAC share一个CLI, 这里相当于这个镜像已经保存在本地.
10.通过Dockerfile Build虚拟镜像(Docker CLI)
1).这里我们通过Docker脚本在原有centos67base/apache镜像基础上安装vim, 实现一个简单的自动化部署.
# cd ~/Work/Docker/Build
# vi Dockerfile
FROM centos67base/apache:apache_base RUN yum install vim -y
TIP: 这里centos67base/apache:apache_base对应REPOSITORY:TAG
2).开始Build
# docker build -t centos67base/apache/vim .
Sending build context to Docker daemon 416.2 MB Step 1 : FROM centos67base/apache:apache_base ---> d9aef85ea282 Step 2 : RUN yum install vim -y ---> Running in 721c0cd73025 Loaded plugins: fastestmirror Setting up Install Process Determining fastest mirrors * base: mirrors.yun-idc.com * extras: mirrors.yun-idc.com * updates: mirrors.yun-idc.com Resolving Dependencies --> Running transaction check ---> Package vim-enhanced.x86_64 2:7.4.629-5.el6 will be installed --> Processing Dependency: vim-common = 2:7.4.629-5.el6 for package: 2:vim-enhanced-7.4.629-5.el6.x86_64 --> Processing Dependency: which for package: 2:vim-enhanced-7.4.629-5.el6.x86_64 --> Processing Dependency: perl(:MODULE_COMPAT_5.10.1) for package: 2:vim-enhanced-7.4.629-5.el6.x86_64 --> Processing Dependency: libperl.so()(64bit) for package: 2:vim-enhanced-7.4.629-5.el6.x86_64 --> Processing Dependency: libgpm.so.2()(64bit) for package: 2:vim-enhanced-7.4.629-5.el6.x86_64 --> Running transaction check ---> Package gpm-libs.x86_64 0:1.20.6-12.el6 will be installed ---> Package perl.x86_64 4:5.10.1-141.el6_7.1 will be installed --> Processing Dependency: perl(version) for package: 4:perl-5.10.1-141.el6_7.1.x86_64 --> Processing Dependency: perl(Pod::Simple) for package: 4:perl-5.10.1-141.el6_7.1.x86_64 --> Processing Dependency: perl(Module::Pluggable) for package: 4:perl-5.10.1-141.el6_7.1.x86_64 ---> Package perl-libs.x86_64 4:5.10.1-141.el6_7.1 will be installed ---> Package vim-common.x86_64 2:7.4.629-5.el6 will be installed --> Processing Dependency: vim-filesystem for package: 2:vim-common-7.4.629-5.el6.x86_64 ---> Package which.x86_64 0:2.19-6.el6 will be installed --> Running transaction check ---> Package perl-Module-Pluggable.x86_64 1:3.90-141.el6_7.1 will be installed ---> Package perl-Pod-Simple.x86_64 1:3.13-141.el6_7.1 will be installed --> Processing Dependency: perl(Pod::Escapes) >= 1.04 for package: 1:perl-Pod-Simple-3.13-141.el6_7.1.x86_64 ---> Package perl-version.x86_64 3:0.77-141.el6_7.1 will be installed ---> Package vim-filesystem.x86_64 2:7.4.629-5.el6 will be installed --> Running transaction check ---> Package perl-Pod-Escapes.x86_64 1:1.04-141.el6_7.1 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: vim-enhanced x86_64 2:7.4.629-5.el6 base 1.0 M Installing for dependencies: gpm-libs x86_64 1.20.6-12.el6 base 28 k perl x86_64 4:5.10.1-141.el6_7.1 updates 10 M perl-Module-Pluggable x86_64 1:3.90-141.el6_7.1 updates 40 k perl-Pod-Escapes x86_64 1:1.04-141.el6_7.1 updates 33 k perl-Pod-Simple x86_64 1:3.13-141.el6_7.1 updates 213 k perl-libs x86_64 4:5.10.1-141.el6_7.1 updates 579 k perl-version x86_64 3:0.77-141.el6_7.1 updates 52 k vim-common x86_64 2:7.4.629-5.el6 base 6.7 M vim-filesystem x86_64 2:7.4.629-5.el6 base 15 k which x86_64 2.19-6.el6 base 38 k Transaction Summary ================================================================================ Install 11 Package(s) Total download size: 19 M Installed size: 59 M Downloading Packages: -------------------------------------------------------------------------------- Total 7.0 MB/s | 19 MB 00:02 Running rpm_check_debug Running Transaction Test Transaction Test Succeeded Running Transaction Installing : 1:perl-Pod-Escapes-1.04-141.el6_7.1.x86_64 1/11 Installing : 1:perl-Module-Pluggable-3.90-141.el6_7.1.x86_64 2/11 Installing : 3:perl-version-0.77-141.el6_7.1.x86_64 3/11 Installing : 4:perl-libs-5.10.1-141.el6_7.1.x86_64 4/11 Installing : 1:perl-Pod-Simple-3.13-141.el6_7.1.x86_64 5/11 Installing : 4:perl-5.10.1-141.el6_7.1.x86_64 6/11 Installing : 2:vim-filesystem-7.4.629-5.el6.x86_64 7/11 Installing : 2:vim-common-7.4.629-5.el6.x86_64 8/11 Installing : which-2.19-6.el6.x86_64 9/11 Installing : gpm-libs-1.20.6-12.el6.x86_64 10/11 Installing : 2:vim-enhanced-7.4.629-5.el6.x86_64 11/11 Verifying : 1:perl-Pod-Simple-3.13-141.el6_7.1.x86_64 1/11 Verifying : 1:perl-Pod-Escapes-1.04-141.el6_7.1.x86_64 2/11 Verifying : gpm-libs-1.20.6-12.el6.x86_64 3/11 Verifying : which-2.19-6.el6.x86_64 4/11 Verifying : 2:vim-enhanced-7.4.629-5.el6.x86_64 5/11 Verifying : 2:vim-filesystem-7.4.629-5.el6.x86_64 6/11 Verifying : 1:perl-Module-Pluggable-3.90-141.el6_7.1.x86_64 7/11 Verifying : 2:vim-common-7.4.629-5.el6.x86_64 8/11 Verifying : 3:perl-version-0.77-141.el6_7.1.x86_64 9/11 Verifying : 4:perl-libs-5.10.1-141.el6_7.1.x86_64 10/11 Verifying : 4:perl-5.10.1-141.el6_7.1.x86_64 11/11 Installed: vim-enhanced.x86_64 2:7.4.629-5.el6 Dependency Installed: gpm-libs.x86_64 0:1.20.6-12.el6 perl.x86_64 4:5.10.1-141.el6_7.1 perl-Module-Pluggable.x86_64 1:3.90-141.el6_7.1 perl-Pod-Escapes.x86_64 1:1.04-141.el6_7.1 perl-Pod-Simple.x86_64 1:3.13-141.el6_7.1 perl-libs.x86_64 4:5.10.1-141.el6_7.1 perl-version.x86_64 3:0.77-141.el6_7.1 vim-common.x86_64 2:7.4.629-5.el6 vim-filesystem.x86_64 2:7.4.629-5.el6 which.x86_64 0:2.19-6.el6 Complete! ---> dfd4d0e4442c Removing intermediate container 721c0cd73025 Successfully built dfd4d0e4442c
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE centos67base/apache/vim latest dfd4d0e4442c 10 seconds ago 327 MB centos67base/apache apache_base d9aef85ea282 2 weeks ago 233.6 MB
这里我们就在之前镜像的基础上Build出一个新的image, 有特殊需求的同学可以在此脚本的基础上编写更加详细的部署步骤.
更多配置可以参考官方文档: https://docs.docker.com/engine/reference/builder/
11. 本地与容器间传输文件
我们可以通过docker内建命令docker cp对已创建好的容器来进行文件传输.
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3d9136b53a7f 8355bdcdcde0 "/bin/bash" 6 minutes ago Up 6 minutes centos67base
# ls
Dockerfile
# docker cp Dockerfile centos67base:/Dockerfile
# docker attach centos67base
# ls
Dockerfile boot etc lib media opt root selinux sys usr bin dev home lib64 mnt proc sbin srv tmp var
12. 访问容器端口
如果我们需要在本地MAC访问容器的具体服务, 我们可以通过DOCKER的NAT Forwarding来实现访问该容器端口服务.
这里我们可以利用之前安装的apache实现MAC本地访问容器的apache web服务.
1). 创建apache容器, 并打开其80 NAT Forwarding (Docker CLI)
# docker run -i -t --privileged -p 80:80 d9aef85ea282 /bin/bash
Tip: 80:80 前者为母虚拟机端口, 后者为子虚拟容器端口
2). 登陆进容器, 开启apache服务并登出(子虚拟容器)
# service httpd start
# echo "This is a test web page" > /var/www/html/index.html
Ctrl+P, 然后Ctrl+Q登出
3).访问该服务(Docker CLI)
查看Docker母虚拟机IP信息
# docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM ERRORS default * virtualbox Running tcp://192.168.99.101:2376
查看子容器NAT配置
# docker ps -a
# docker port cce502d8a21a
80/tcp -> 0.0.0.0:80
这里可以看到NAT已配置成功
成功访问web服务
# curl http://192.168.99.101/
This is a test web page!!!!
TIP: 这里由于我们实际上是在VirtualBox下的一个Docker instance虚拟机下, 所以访问的IP为192.168.99.101, 而不是本地127.0.0.1.
这里再次给同学们强调虽然我们的目录结构感觉是在本地, 但实际上我们处在Docker虚拟机下, 只是这个虚拟机嵌入到本地目录中.
至此我们大致将SA的Docker原理, 制作过程和配置使用详细讲解给大家, 其中借鉴了很多老外的部署经验, 希望有英语基础的同学可以去国外的技术网站去获取相关的知识. 随后我会陆续更新Docker自动化部署等文档.
Hope you enjoy…
本文链接:http://www.showerlee.com/archives/1758
继续浏览:Docker
又快过年了,咿呀咿呀呦
乙未年(羊)冬月十四 2015-12-24