docker管理数据(二):volumes

Comentarios

docker管理数据(一):如何管理数据

介绍

为什么需要着重讲一讲docker中的数据管理,在实际的运行过程中,经常需要存储一些配置信息,程序运行中产生的信息,这些都是需要持久化的数据。可以将这些数据写入容器的文件层中,这样的方式有很多的缺点:

  • 当容器生命周期结束之后,这些数据也会同步丢失;对于存储在容器文件层中的数据,从容器外获取非常的不方便。
  • 容器的可写层和宿主机的耦合非常的严重,很难一致到别的地方执行。
  • 将数据写入可写层需要storage driver的支持,这种方式和使用volumes相比,会有极大的性能损失。

Docker提供三种不同的方式将宿主机的文件映射到容器内部,用于读写数据:

  • volumes
  • bind mounts
  • tmpfs

当你不确定使用那一种方式的时候,volumes总会是一种不错的选择。

选择合适的存储

不管选择哪一种数据存储方式,在容器内部,这些数据的使用方式是一致的。要么以文件夹的方式,要么以文件的方式在容器内部展现。

下面这幅图清楚的展示了三种方式存储数据的不同:

  • Volumes:数据存储在由Docker管理的宿主机的文件系统上( /var/lib/docker/volumes/),非Docker进程不能修改这一部分的内容。Volumes是最好的管理数据的方式。
  • Bind mounts:数据存储在宿主机的任何地方,非Docker进程也可以修改这些区域的数据。
  • tmpfs:数据存储在宿主机的内存中,并不会持久化到磁盘上。

什么时候选择volumes

Volumes始终是推荐的管理数据的方式,一些典型的应用场景如下:

  • 在多个运行的容器之间共享数据。如果你没有显示的创建它,volume会在第一次被mounted到一个容器的时候自动创建。当容器停止甚至被删除,volume仍然会被保存。多个容器可以同时mounted到同一个volume,可以分配读写或者只读权限。volume只会被显示的删除。
  • 当容器的宿主机无法保证一个存在的文件或者文件目录。volumes帮助宿主机的配置和容器运行时的解耦。
  • 当数据不想保存在本地,需要保存到一个远程机器或者是云上。
  • 当需要从一台宿主机备份、恢复、迁移数据到另外一台宿主机时,volumes是一个很好的选择。

什么时候选择bind mounts

一般情况下请选择使用volumes,在如下情况可以选择bind mounts:

  • 从宿主机共享配置文件到容器。Docker使用这种方式向各个容器共享DNS配置文件/etc/resolv.conf
  • 将开发环境的源码以及编译的文件共享给容器。例如,可以将maven的target/目录共享给容器,每一次编译工程之后,容器内部就可以从新构建服务。如果这种开发步骤,production Dockerfile需要直接拷贝production-ready artifacts

什么时候选择tmpfs

当既不想持久化数据到宿主机,也不想持久化到容器内部可以使用tmpfs。例如,当你的应用程序需要输出大量的状态数据,为了性能或者安全性考虑并不想持久化这些数据时,可以使用tmpfs。

一点小建议

无论使用volume或者mounts,记住以下几点:

  • 如果你映射一个空的volume到容器内部某一个已存在的文件或者文件夹,这些文件或者文件夹会被拷贝到volume中。相似的,如果你启动一个容器,指定一个不存在的volume,会为你创建一个空的volume。这是一种很好的为容器初始化数据的方式。
  • 如果你映射一个非空的volume,或者bind mount到容器内部某一个已存在的文件或者文件夹,这些文件或者文件夹会变成暂时不可访问,直到解除mount关系,这些文件或者文件夹是不会被删除的。
Comentarios

docker容器的网络

准备工作

  • 安装docker

使用默认的网络启动一个容器

Docker通过 network drivers支持容器的网络环境。默认情况下,Docker支持bridgeoverlay两种网络驱动。也可以支持自定义的网络驱动。

每一个Docker Engine都会自动支持三种默认的网络,可以使用docker network ls列出所有的网络驱动:

1
2
3
4
5
6
$ docker network ls
NETWORK ID NAME DRIVER
18a2866682b8 none null
c288470c46f6 host host
7b369448dccb bridge bridge

bridge是一个特殊的网络驱动,如果不做特别指定,所有的容器都会在bridge下启动。尝试一下如下的命令:

1
2
$ docker run -itd --name=networktest ubuntu
74695c9cea6d9810718fddadc01a727a5dd3ce6a69d09752239736c030599741

通过docker network inspect bridge命令可以查看一下容器的网络信息

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
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.1/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Containers": {
"3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c": {
"Name": "networktest",
"EndpointID": "647c12443e91faf0fd508b6edfe59c30b642abb60dfab890b4bdccee38750bc1",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "9001"
},
"Labels": {}
}
]

从一个网络中移除一个容器,如下

1
$ docker network disconnect bridge networktest

以上的命令将networktest容器从bridge网络中移除。使用网络可以方便的将容器隔离。

创建自定义网桥

Docker Engine支持bridgeoverlay模式的网络,bridge模式只能在单机上使用,overlay支持多机使用。接下来,使用bridge创建自定义的网络,

1
$ docker network create -d bridge my_bridge

-d参数指定网络的模式,如果不加-d参数,默认也使用bridge模式。检查一下是否创建成功,

1
2
3
4
5
6
7
$ docker network ls
NETWORK ID NAME DRIVER
7b369448dccb bridge bridge
615d565d498c my_bridge bridge
18a2866682b8 none null
c288470c46f6 host host

使用inspect命令检查新建网络的信息,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ docker network inspect my_bridge
[
{
"Name": "my_bridge",
"Id": "5a8afc6364bccb199540e133e63adb76a557906dd9ff82b94183fc48c40857ac",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "10.0.0.0/24",
"Gateway": "10.0.0.1"
}
]
},
"Containers": {},
"Options": {},
"Labels": {}
}
]

隔离的网络让应用更加安全

接下来,用一个简单的web应用展示如何安全的使用容器的网络。

第一步,在自定义的网络上创建数据库容器,

1
$ docker run -d --net=my_bridge --name db training/postgres

--net参数指定使用的网络,检查是否创建成功,

1
2
3
4
5
$ docker inspect --format='{{json .NetworkSettings.Networks}}' db
{"my_bridge":{"NetworkID":"7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99",
"EndpointID":"508b170d56b2ac9e4ef86694b0a76a22dd3df1983404f7321da5649645bf7043","Gateway":"10.0.0.1","IPAddress":"10.0.0.254","IPPrefixLen":24,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02"}}

第二步,在默认的网络下创建web应用,

1
$ docker run -d --name web training/webapp python app.py

检查网络信息,

1
2
3
4
5
$ docker inspect --format='{{json .NetworkSettings.Networks}}' web
{"bridge":{"NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
"EndpointID":"508b170d56b2ac9e4ef86694b0a76a22dd3df1983404f7321da5649645bf7043","Gateway":"172.17.0.1","IPAddress":"10.0.0.2","IPPrefixLen":24,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02"}}

查看web的地址,

1
2
3
4
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
172.17.0.2

当前的网络拓扑图如下,

第三步,查看网络的连通性,

1
2
3
4
5
6
7
8
$ docker exec -it db bash
root@a205f0dd33b2:/# ping 172.17.0.2
ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
^C
--- 172.17.0.2 ping statistics ---
44 packets transmitted, 0 received, 100% packet loss, time 43185ms

会发现在db上,无法连通到web上。

第四步,将web连接到my_bridge上,

1
$ docker network connect my_bridge web

当前的网络拓扑,

第五步,再一次验证网络的连通性,

1
2
3
4
5
6
7
8
9
10
11
$ docker exec -it db bash
root@a205f0dd33b2:/# ping web
PING web (10.0.0.2) 56(84) bytes of data.
64 bytes from web (10.0.0.2): icmp_seq=1 ttl=64 time=0.095 ms
64 bytes from web (10.0.0.2): icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from web (10.0.0.2): icmp_seq=3 ttl=64 time=0.066 ms
^C
--- web ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.060/0.073/0.095/0.018 ms

会发现db和web已经能够正常的连通。其它不在my_bridge上的容器不能连接到该网络环境中

总结

通过自定义网络环境,可以将安全性要求较高的服务,放入单独的自定义的网络环境中,以此保证服务的安全行。

Comentarios

学习DOCKERFILE(三):编写dockerfile的一些建议

准备工作

常用的原则

  • 原则一:容器的生命周期越短越好

所谓的生命周期越短越好,是要强调,通过dockerfile定义的镜像,可以使用最少的步骤和配置,很方便的停止、销毁、构建、部署,达到无状态的模式。

  • 原则二:使用.dockerignore文件

docker build指令运行的上下文环境有两种,其一,docker build运行的当前目录以及所有的子目录;其二,-f命令指定的目录及所有的子目录。当运行docker build命令时,上下文环境中的所有文件及其目录都会被送到docker deamon中,被认为是编译的上下文环境。上下文中的文件越多越大,编译所需要的时间,以及最终编译出来的image就会越大。也会直接的导致,pull、push以及run这个image的时间会越长。下面的这条信息告知了docker build上下文环境的大小,

1
Sending build context to Docker daemon 187.8MB

一些编译环境的文件也不能被删除,为了优化这个问题,docker提供了.dockerginore文件,它如同.gitignore文件一样,支持排除模式。

1
2
3
4
# comment
*/temp*
*/*/temp*
temp?
Rule Behavior
# comment 忽略
*/temp* 根目录的二级子目录中,以temp开头的文件或者文件夹,都会被忽略。例如,/somedir/temporary.txt 将会被忽略。
*/*/temp* 根目录的三级子目录中,以temp开头的文件或者文件夹,都会被忽略。例如,/somedir/subdir/temporary.txt 将会被忽略。
temp? 根目录下的以 /tempa 开头的文件或者文件夹,都会被忽略。
  • 原则三:使用muitl-stage多阶段构建

使用muitl-stage多阶段构建,可以有效的减少了image的大小,增加了dockerfile,部署的简洁性和可维护性。

  • 原则四:避免安装不必要的包

为了减少复杂性、依赖、文件大小以及构建时间,最好避免安装不必要的包。例如,数据库的image中没有必要安装文件编辑工具。

  • 原则五:每个容器应该只做一件事情

将一个大的应用解耦分拆到不同的容器中,使其更好的水平扩展、重用。例如,一个web应用栈可以分为三个独立的容器,一个容器用做web界面,一个用做数据库存储,一个用于in-memory的缓存。

你可能听说过“一个容器就是一个进程”,这句话虽有夸张的成分,但是很好的解释了,使用容器的范式。尽量让容器功能单一,如果容器之间存在依赖,可以使用docker container networks相互通信。

  • 原则六:容器的文件层越少越好,减少到不能再少

docker 17.05以及1.10以前,减少文件层非常的重要。比较高版本的docker优化了这些问题;

docker 1.10 以及高版本,只有RUN, COPY以及 ADD指令 会增加文件层,其它指令会创建临时的中间镜像,不会增加最终的image的大小。

Docker 17.05 以及高版本,增加了multi-stage特性,允许只拷贝想要的文件到最终的镜像。

  • 原则七:将多参数的命令行排序

只要有可能,将多参数的命令行安装字母顺序排序,保证格式清楚,可阅读性高。例如:

1
2
3
4
5
6
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
  • 原则八:使用缓存

image的构建过程,就是一步一步按照指定的顺序执行dockerfile中的命令。每执行一条命令,docker会先在cache中查找,是否有以及存在可复用的缓存,如果没有,则会创建新的image。如果不想使用缓存功能,可以在运行docker build命令是指定--no-cache=true参数。

缓存的查找规则如下,

  1. 使用一个已经存在在缓存中的镜像最为父镜像,紧接着的一条命令,将会和此父镜像衍生的子镜像做对比,如果命令不同,则cache失效。
  2. 在大多数情况下,只需要简单的比较dockerfile文件是否相同。在特殊的情况下,需要更复杂的对比。
  3. 对于ADDCOPY命令来说,需要对比文件的内容,并会给每一个文件计算一个hash值,最近修改时间,以及访问次数,不参与hash值的计算。查找的过程,就是对比hash值是否相同,如果不同,则cache失效。
  4. 除去 ADDCOPY命令,其它的命令只会对比命令字符串本身。

常用的指令

FROM

尽可能使用官方仓库最为自己的基础镜像,推荐使用Debian image

LABEL

仅可能给自己的镜像加上标签,方面管理,添加版权信息等。如下提供了一个标准的例子:

1
2
3
4
5
# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor="ACME Incorporated"
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

在dockr 1.10版本之前,推荐所有的标签写入一行,以便减少镜像的文件层。新版本无需特殊的考虑,以前版本的例子:

1
2
# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

或者

1
2
3
4
5
6
# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
com.example.is-beta= \
com.example.is-production="" \
com.example.version="0.0.1-beta" \
com.example.release-date="2015-02-12"

RUN

尽可能将命令写入一行,对于多行命令,使用/连接成一行。例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.* \
&& rm -rf /var/lib/apt/lists/*

CMD

CMD命令用来启动容器中的应用程序,并且可以传人一些参数。一般的使用格式CMD [“executable”, “param1”, “param2”…],对于service类型的应用,比如apache和rails,使用方式 CMD ["apache2","-DFOREGROUND"]

对于其他类型的应用, CMD应该启动一个常用的交互shell,就像bash,python以及perl。例如CMD ["perl", "-de0"], CMD ["python"], 或者 CMD [“php”, “-a”]。这样就能保证,当你运行docker run -it python就能进入一个可用的shell环境。除非你对 ENTRYPOINT命令非常的熟悉,最好不要用如下的格式CMD [“param”, “param”]配合 ENTRYPOINT使用。

EXPOSE

EXPOSE命令指定了容器中应用程序监听的端口号,最好使用应用程序常用的端口号,比如 Apache web server一般使用EXPOSE 80,MongoDB使用EXPOSE 27017

对于外部访问来说,可以使用docker run -p 8080:80映射到宿主机上的端口,对于容器的互连,docker也提供了环境变量的方式,比如MYSQL_PORT_3306_TCP

ENV

为了使新的软件更容易运行,可以使用ENV命令设置容器的环境变量,比如,ENV PATH /usr/local/nginx/bin:$PATH 将nginx设置到环境变量中, CMD [“nginx”] 可以直接运行。

也可以设置环境变量,给应用程序使用,如下:

1
2
3
4
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

ADD/COPY

尽管ADDCOPY命令功能基本相同,仍然推荐优先使用COPY命令。COPY命令只支持基本的拷贝功能,ADD有一些隐式的特性,支持打包文件的解压,指定网络路径等等。

如果你需要拷贝一些文件到容器中,最好单独的拷贝,不要一次性全拷贝,比如下面的例子:

1
2
3
COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/

如果COPY . /tmp/放在run之前,会大大的增加cache失效的可能性。

从镜像大小的角度考虑,尽量避免使用ADD命令获取远程的资源,可以使用curl或者wget代替。看看下面的例子:

1
2
3
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

替换为:

1
2
3
4
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all

ENTRYPOINT

VOLUME

VOLUME 指令用来将需要持久化的数据容器暴露到容器外,比如数据库的数据文件,配置文件或者其它的用户创建的文件或者目录。强烈推荐使用 VOLUME 命令保存有状态的数据。

USER

如果一个服务并不需要一些root权限,可以使用USER命令切换到非root用户。比如接下来的命令:RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

应该避免使用sudo命令,sudo命令会带来TTY以及信号方面的问题。如果确实需要sudo的功能,推荐使用“gosu”。

最后一点,为了避免文件层以及复杂性,避免来回切换账号。

WORKDIR

为了明确和清晰,尽量为工作目录指定绝对路径,不要使用相对路径。

ONBUILD

ONBUILD命令会在当前的 Dockerfile构建完成之后执行。所有的子镜像都会执行父镜像的ONBUILD,然后才会构建自己的 Dockerfile

Comentarios

学习dockerfile(二):多阶段构建

准备工作

介绍

multi-stage功能有什么功效?接下来将通过一个go程序例子进行讲解。开发go应用程序需要如下几个必备条件;

  • 一台用于开发的机器(linux、window)
  • go开发环境,设置goroot、gopath,gobin等环境变量
  • 额外的lib库
  • 运行binary环境

如果不使用multi-stage,为了尽量保证image比较小,可能会使用如下的方式构建docker image。

  • 第一步,创建用于编译环境的Dockerfile.build
1
2
3
4
5
6
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
  • 第二步,创建用于运行环境的Dockerfile
1
2
3
4
5
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
  • 第三步,创建运行脚步build.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
echo Building alexellis2/href-counter:build
docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
-t alexellis2/href-counter:build . -f Dockerfile.build
docker create --name extract alexellis2/href-counter:build
docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker rm -f extract
echo Building alexellis2/href-counter:latest
docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app

以上的构建过程,运行build.sh,会先构建编译环境用于构建可运行的应用程序,然后,将可执行应用程序app拷贝到local,用于构建最终的运行环境,最后,清理local的app临时文件。

整个构建过程,需要编写额外的build.sh脚步,执行冗长的命令,最后还需要做一些清理工作。不管是用于编译的image还是用于运行的image都会占用本地的磁盘空间。

那么,有没有更好的方式?答案就是multi-stage

使用multi-stage构建

编写dockerfile

1
2
3
4
5
6
7
8
9
10
11
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

dockerfile中,使用两次FROM命令,每一个FROM可以使用不同的基础image,它们处于不同的构建阶段。可以从一个stage拷贝文件到另外一个stage,使用COPY命令,--from指定从哪个stage拷贝,不需要的文件可以完全忽略。

构建命令如下,

1
docker build -t alexellis2/href-counter:latest .

给stage命名

默认情况下,每一个stage是没有命名的,只能通过序号进行引用,第一个FROM的序号是0,后面的依次类推。也可以通过给stage命名,更方便的引用各个stage,并增加可维护性,避免重新排列FROM命令造成混乱。接下来通过修改上面的例子展示了stage命名的用法;

1
2
3
4
5
6
7
8
9
10
11
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

总结

multi-stage模式有效的减少了image的大小,增加了dockerfile,部署的简洁性和可维护性。

Comentarios

学习dockerfile(一):创建一个基础镜像

前两周看完了docker tutorial官方系列教程,了解了docker是什么,docker的使用场景,为什么要使用docker以及如何使用docker。接下来的一周,需要深入一点,修炼一些docker的基本功,比如,如何写好dockerfile。
这一系列的教程有如下几篇文档:

  1. 创建一个基础镜像
  2. 多阶段构建
  3. 管理镜像
  4. 编写Dockerfile最佳实践

绝大多数的Dockerfile都会基于已有的基础镜像进行构建,如果你需要完全控制镜像的内容,从空白的镜像创建,需要使用FROM scratch,或者不使用FROM指令。

使用FROM scratch创建镜像

scratch镜像是Docker保留的最小镜像。使用scratch开始创建,scratch的下一条命令,将会是镜像的第一层。

  • 创建一个linux环境
1
2
3
4
$ docker run --rm -it -v $PWD:/build ubuntu:16.04
container# apt-get update && apt-get install build-essential
container# cd /build
container# gcc -o hello -static -nostartfiles hello.c
  • 创建一个hello-world镜像
1
2
3
FROM scratch
ADD hello /
CMD ["/hello"]

总结

这篇文章介绍了如何使用docker创建一个简单的linux的编译环境,编译一个简单的hello-world二进制文件,并将这个二进制文件加入到空白的镜像中执行。

Comentarios

使用docker构建hexo风格的个人主页

准备工作

  • docker环境
  • nginx
  • hexo

生成hexo网站

hexo创建一个html的静态网站

1
2
hexo init blog
hexo generate

编写Dockerfile

1
2
FROM nginx
COPY . /usr/share/nginx/html

基于nginx基础镜像,将hexo生成的静态网站copy到镜像中。注意,html文件和Dockerfile文件在同一个目录。

生成image

1
docker build -t hexo-nginx .

上传镜像

  • 登录
1
docker login
  • 打tag
1
docker tag hexo-nginx ruccsbingo/hexo-nginx:1.0
  • 上传镜像
1
docker push ruccsbingo/hexo-nginx:1.0

部署容器应用

登录远程的服务器,执行一下命令,从docker hub上下载生成好的镜像,直接启动部署。

1
docker run -p 80:80 -d ruccsbingo/hexo-nginx:1.0

浏览器访问

Comentarios

Docker入门系列(六):最后的总结

经过一段时间的思考,终于下定决心好好的学习一下docker。找到官网的教程,前前后后花了一周的时间,按照教程一步一步的敲命令,完整的搭建了一个完整的swarm集群。

在做的过程中,遇到很多的问题,

  • 第一个最让人头疼的问题,就是国内网络访问docker hub速度感人,经常出现各种各样的莫名其妙的问题,刚开始的时候,也不懂在哪里找error log。明明很简单的一个命令,就是没有预定的结果,后来发现,所有的奇怪问题,基本都和网络有关,一般都是想要下载的image没有下载下来。
  • 第二个就需要仔细阅读官方教程,仔细理解docker各个组件的概念,区别docker client, docker deamon,swarm manager,worker。
  • 第三个端口的问题,区别docker deamon的端口,和swarm manager的端口。
  • 最后,要好好理解docker repository,了解docker pull的执行流程。

针对上面提到的第一个和第三个问题,都在文档中做了记载,已备后来者。

第一个问题的解决方案,设置阿里云镜像

第三个问题,关于2377和2376的区别,在docker入门系列(四)中有提到。。

最后的总结

看介绍文章,和一步一步实践,一行一行敲命令,还是有很大的区别。以前总是看一些技术性的介绍文章,觉得自己什么都懂了,什么概念都清楚了,开口闭口都是容器化,服务编排。等到一个任务交给你去做的时候,总是无从下手,这次动手实践,真真实实的感受到了,docker给编程,或者说是软件流程的革命性的变化,一件搭建编程环境,一键部署应用,几代工程师的梦想,看来越来越接近现实了。

Comentarios

Mac上配置阿里云的docker镜像

需求

由于众所周知的原因,在国内使用docker hub的体验让人奔溃,网速太慢,代理不稳定。好在可以使用国内镜像,接下来,记录一下,使用阿里云镜像的步骤。

安装/升级你的Docker客户端

  • 对于10.10.3以下的用户 推荐使用
1
Docker Toolbox

如何使用Docker加速器

  1. 创建一台安装有Docker环境的Linux虚拟机,指定机器名称为default,同时配置Docker加速器地址。

    1
    docker-machine create --engine-registry-mirror=https://ykf2xy9h.mirror.aliyuncs.com -d virtualbox default
  2. 查看机器的环境配置,并配置到本地,并通过Docker客户端访问Docker服务。

    1
    2
    3
    docker-machine env default
    eval "$(docker-machine env default)"
    docker info
  3. 执行hello-world,验证是否配置成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ docker run hello-world
    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    To generate this message, Docker took the following steps:
    1. The Docker client contacted the Docker daemon.
    2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
    4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.
    To try something more ambitious, you can run an Ubuntu container with:
    $ docker run -it ubuntu bash
    Share images, automate workflows, and more with a free Docker ID:
    https://cloud.docker.com/
    For more examples and ideas, visit:
    https://docs.docker.com/engine/userguide/

恭喜你,可以愉快使用docker镜像了。

Comentarios

Docker入门系列(五):增加一个可持久化的访问计数

准备工作

介绍

第四节中,介绍了如何启动swarm,如何将服务部署到多台机器之上。在这一节中,将着重介绍stack,所谓的stack就是一组相互关联的服务,它们能够共享一些依赖,能够并一起编排和扩容。在第三节中,介绍了一个单服务的stack,这个stack中只有一个服务,只运行在一台宿主机上。在这一节中,将介绍多服务的stack,并运行在多台机器之上。

添加一个新的服务并部署

添加服务非常的简单,只需要编辑docker-compose.yml,添加相关的服务信息。比如,给swarm机器添加一个可视化的服务,展示swarm集群的机器和服务信息。

  1. 编辑docker-compose.yml
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
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: ruccsbingo/get-started:part2
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:

docker-compose.yml中增加了visualizer的相关配置项。

  1. 配置shell,连接上myvm1的docker环境
  2. 在swarm manager上执行docker stack deploy重新部署服务
1
2
3
4
$ docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
Creating service getstartedlab_visualizer
  1. 在浏览器中验证visualizer是否安装成功

B5308526-E53E-411C-9D85-86272C1B38D9

持久化数据

重复上面的过程,在给我们的stack中添加redis服务。

  1. 编辑docker-compose.yml添加redis的依赖
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
41
42
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: ruccsbingo/get-started:part2
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
redis:
image: redis
ports:
- "6379:6379"
volumes:
- /home/docker/data:/data
deploy:
placement:
constraints: [node.role == manager]
command: redis-server --appendonly yes
networks:
- webnet
networks:
webnet:

Note:

  • image: redis,在Docker library中有redis的官方镜像,因此,此处可以使用简称
  1. 在manager上创建data目录,用于持久化redis中的数据
1
docker-machine ssh myvm1 "mkdir ./data"
  1. 确保当前shell环境连上manager,接下来的命令都需要在manager上执行
  2. 在manager上执行docker stack deploy
1
$ docker stack deploy -c docker-compose.yml getstartedlab
  1. 执行docker service ls验证服务启动情况
1
2
3
4
5
docker@myvm1:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
nylj70biukz0 getstartedlab_redis replicated 1/1 redis:latest *:6379->6379/tcp
mval7ra97snr getstartedlab_visualizer replicated 1/1 dockersamples/visualizer:stable *:8080->8080/tcp
3qutabj1gipx getstartedlab_web replicated 5/5 ruccsbingo/get-started:part2 *:4000->80/tcp
  1. 在浏览器中验证redis中的计数情况

  1. 查看visualizer,观察机器中的服务

恭喜你,一个完成的stack配置完成了。

Comentarios