查看原文
其他

Ubuntu 17.04 x64 安装 Docker CE 初窥 Dockerfile 部署 Nginx

搜云库 搜云库技术团队 2019-04-07

Docker 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本!使用 Docker,可以让应用的部署、测试和分发都变得前所未有的高效和轻松!

无论是应用开发者、运维人员、还是其他信息技术从业人员,都有必要认识和掌握 Docker,节约有限的时间。

系统要求

要安装 DockerCE,您需要这些 Ubuntu版本的64位版本:

  • Artful 17.10(Docker CE 17.11 Edge及更高版本)

  • ZESTY 17.04

  • Xenial 16.04(LTS)

  • Trusty 14.04(LTS)

Ubuntux86_64Linuxarmhfs390xIBM Z)和ppc64leIBMPower)架构上支持DockerCE 。

卸载旧版本

老版本的 Docker被称为 docker或 docker-engine。如果安装了这些,请将其卸载:

  1. $ apt-get remove docker docker-engine docker.io

使用存储库进行安装

首次在新的主机上安装 DockerCE之前,需要设置 Docker存储库。之后,您可以从存储库安装和更新 Docker

  • 设置存储库

1.更新 apt软件包索引:

  1. $ apt-get update

2.安装软件包以允许 apt通过 HTTPS使用存储库:

  1. $ apt-get install \

  2.    apt-transport-https \

  3.    ca-certificates \

  4.    curl \

  5.    software-properties-common

3.添加 Docker的官方 GPG密钥:

  1. $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

9DC858229FC7DD38854AE2D88D81803C0EBFCD88通过搜索指纹的最后8个字符,确认您现在拥有指纹的密钥 。

  1. $ sudo apt-key fingerprint 0EBFCD88

  2. pub   rsa4096 2017-02-22 [SCEA]

  3.      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88

  4. uid           [ unknown] Docker Release (CE deb) <docker@docker.com>

  5. sub   rsa4096 2017-02-22 [S]

安装Docker CE

1.更新apt软件包索引。

  1. $ apt-get update

2.安装最新版本的Docker CE,或者转到下一步安装特定版本。任何现有的Docker安装都将被替换。

  1. $ apt-get install docker-ce

3.在生产系统上,您应该安装特定版本的Docker CE,而不是始终使用最新版本。此输出被截断。列出可用的版本。

  1. $ apt-cache madison docker-ce

  2. docker-ce | 17.12.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

  3. docker-ce | 17.09.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

  4. docker-ce | 17.09.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

  5. docker-ce | 17.06.2~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

  6. docker-ce | 17.06.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

  7. docker-ce | 17.06.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu zesty/stable amd64 Packages

列表的内容取决于启用了哪个存储库。选择一个特定的版本进行安装。第二列是版本字符串。第三列是存储库名称,它指出了软件包来自哪个存储库,并通过扩展其稳定性级别。要安装特定版本,请将版本字符串附加到包名称,并用等号(=)将它们分开:

  1. $ sudo apt-get install docker-ce=<VERSION>

4.通过运行 hello-world 映像验证是否正确安装了 DockerCE 。

  1. $ docker run hello-world

这个命令下载一个测试图像并在容器中运行。容器运行时,会打印一条信息消息并退出。

  1. Unable to find image 'hello-world:latest' locally

  2. latest: Pulling from library/hello-world

  3. ca4f61b1923c: Pull complete

  4. Digest: sha256:445b2fe9afea8b4aa0b2f27fe49dd6ad130dfe7a8fd0832be5de99625dad47cd

  5. Status: Downloaded newer image for hello-world:latest

  6. Hello from Docker!

  7. This message shows that your installation appears to be working correctly.

  8. To generate this message, Docker took the following steps:

  9. 1. The Docker client contacted the Docker daemon.

  10. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.

  11.    (amd64)

  12. 3. The Docker daemon created a new container from that image which runs the

  13.    executable that produces the output you are currently reading.

  14. 4. The Docker daemon streamed that output to the Docker client, which sent it

  15.    to your terminal.

  16. To try something more ambitious, you can run an Ubuntu container with:

  17. $ docker run -it ubuntu bash

  18. Share images, automate workflows, and more with a free Docker ID:

  19. https://cloud.docker.com/

  20. For more examples and ideas, visit:

  21. https://docs.docker.com/engine/userguide/

以非root用户身份管理Docker

默认情况下, docker 命令会使用 Unixsocket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unixsocket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

要创建 docker组并添加您的用户:

1.创建docker组。

  1. $ sudo groupadd docker

2.将您的用户添加到docker组中。

  1. $ sudo usermod -aG docker $USER

3.注销并重新登录,如果在虚拟机上进行测试,则可能需要重新启动虚拟机才能使更改生效。

4.验证您可以不运行 docker命令 sudo

  1. $ docker run hello-world

卸载Docker CE

1.卸载 DockerCE软件包:

  1. $ sudo apt-get purge docker-ce

2.主机上的图像,容器,卷或自定义配置文件不会自动删除。删除所有图像,容器和卷:

  1. $ sudo rm -rf /var/lib/docker

利用 commit 理解镜像构成

这条命令会用 nginx 镜像启动一个容器,命名为 myweb,并且映射了 80 端口,这样我们可以用浏览器去访问这个 nginx 服务器。

  1. $ docker run --name myweb -d -p 80:80 nginx

直接访问:http://localhost;如果使用的是 Docker Toolbox,或者是在虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际云服务器地址。

现在,假设我们非常不喜欢这个欢迎页面,我们希望改成欢迎 Docker 的文字,我们可以使用 dockerexec 命令进入容器,修改其内容。

  1. $ docker exec -it myweb bash

  2. root@5ceb0c8274ca:/# echo '<h1>Welcome to Docker!</h1>' > /usr/share/nginx/html/index.html

  3. root@5ceb0c8274ca:/# exit

  4. exit

直接访问:http://localhost;

我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动。 COMMENT列有备注!

  1. $  docker diff myweb

  2. C /root

  3. A /root/.bash_history

  4. C /run

  5. A /run/nginx.pid

  6. C /usr/share/nginx/html/index.html

  7. C /var/cache/nginx

  8. D /var/cache/nginx/client_temp

  9. D /var/cache/nginx/fastcgi_temp

  10. D /var/cache/nginx/proxy_temp

  11. D /var/cache/nginx/scgi_temp

  12. D /var/cache/nginx/uwsgi_temp

  13. root@souyunku:~/mydocker#

我们可以用下面的命令将容器保存为镜像:

  1. $ docker commit \

  2.    --author "penglei <admin@souyunku.com>" \

  3.    --message "修改了默认网页" \

  4.    myweb \

  5.    nginx:v2

  6. sha256:33b2e2aefccbaba54021c85ef7966c7d488abaa0677728e1b057c56a7734a4f0

其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制相似,不过这里这些信息可以省略留空。

我们可以在 docker image ls 中看到这个新定制的镜像:

  1. $ docker image ls

  2. REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE

  3. nginx               v2                  33b2e2aefccb        About a minute ago   108MB

  4. nginx               latest              3f8a4339aadd        3 days ago           108MB

  5. hello-world         latest              f2a91732366c        5 weeks ago          1.85kB

  1. $ docker history nginx:v2

  2. IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT

  3. 33b2e2aefccb        2 minutes ago       nginx -g daemon off;                            325B                修改了默认网页

  4. 3f8a4339aadd        3 days ago          /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B                  

  5. <missing>           3 days ago          /bin/sh -c #(nop)  STOPSIGNAL [SIGTERM]         0B                  

  6. <missing>           3 days ago          /bin/sh -c #(nop)  EXPOSE 80/tcp                0B                  

  7. <missing>           3 days ago          /bin/sh -c ln -sf /dev/stdout /var/log/nginx…   22B                

  8. <missing>           3 days ago          /bin/sh -c set -x  && apt-get update  && apt…   53.2MB              

  9. <missing>           3 days ago          /bin/sh -c #(nop)  ENV NJS_VERSION=1.13.8.0.…   0B                  

  10. <missing>           3 days ago          /bin/sh -c #(nop)  ENV NGINX_VERSION=1.13.8-…   0B                  

  11. <missing>           2 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B                  

  12. <missing>           2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B                  

  13. <missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:f30a8b5b7cdc9ba33…   55.3MB

新的镜像定制好后,我们可以来运行这个镜像。

  1. $ docker run --name web2 -d -p 81:80 nginx:v2

  2. ed8c54aeb3c540981b892c0cdfbf9330114ecc935149190e073f49295f2ae147

直接访问:http://localhost:81;如果使用的是 Docker Toolbox,或者是在虚拟机、云服务器上安装的 Docker,则需要将 localhost 换为虚拟机地址或者实际云服务器地址。

使用 Dockerfile 定制镜像

从刚才的 docker commit 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile

Dockerfile 是一个文本文件,其内包含了一条条的指令( Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

还以之前定制 nginx 镜像为例,这次我们使用 Dockerfile 来定制。

在一个空白目录中,建立一个文本文件,并命名为 Dockerfile

  1. $ mkdir mynginx

  2. $ cd mynginx

  3. $ touch Dockerfile

其内容为:

  1. FROM nginx

  2. RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

这个 Dockerfile 很简单,一共就两行。涉及到了两条指令, FROM 和 RUN

FROM 指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。

在 DockerStore 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx、 redis、 mongo、 mysql、 httpd、 php、 tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node、 openjdk、 python、 ruby、 golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu、 debian、 centos、 fedora、 alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外, Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

  1. FROM scratch

  2. ...

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm、 coreos/etcd。对于 Linux下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。

RUN 执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:

  • shell 格式: RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。

  1. RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

  • exec 格式:RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。

既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN呢?比如这样:

  1. FROM debian:jessie

  2. RUN apt-get update

  3. RUN apt-get install -y gcc libc6-dev make

  4. RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"

  5. RUN mkdir -p /usr/src/redis

  6. RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1

  7. RUN make -C /usr/src/redis

  8. RUN make -C /usr/src/redis install

之前说过, Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个 RUN的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。

而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。

UnionFS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。

上面的 Dockerfile 正确的写法应该是这样:

  1. FROM debian:jessie

  2. RUN buildDeps='gcc libc6-dev make' \

  3.    && apt-get update \

  4.    && apt-get install -y $buildDeps \

  5.    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \

  6.    && mkdir -p /usr/src/redis \

  7.    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \

  8.    && make -C /usr/src/redis \

  9.    && make -C /usr/src/redis install \

  10.    && rm -rf /var/lib/apt/lists/* \

  11.    && rm redis.tar.gz \

  12.    && rm -r /usr/src/redis \

  13.    && apt-get purge -y --auto-remove $buildDeps

首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对一一对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。

并且,这里为了格式化还进行了换行。 Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。

此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。

构建镜像

好了,让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。

在 Dockerfile 文件所在目录执行:

  1. $ docker build -t nginx:v3 .

  2. Sending build context to Docker daemon  2.048kB

  3. Step 1/2 : FROM nginx

  4. ---> 3f8a4339aadd

  5. Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

  6. ---> Running in 5cdac6d29a52

  7. Removing intermediate container 5cdac6d29a52

  8. ---> 56a1b67b2533

  9. Successfully built 56a1b67b2533

  10. Successfully tagged nginx:v3

从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step2 中,如同我们之前所说的那样, RUN 指令启动了一个容器 5cdac6d29a52,执行了所要求的命令,并最后提交了这一层 56a1b67b2533,随后删除了所用到的这个容器 5cdac6d29a52

这里我们使用了 docker build 命令进行镜像构建。其格式为:

  1. $ docker build [选项] <上下文路径/URL/->

镜像构建上下文(Context)如果注意,会看到 docker build 命令最后有一个 .。 . 表示当前目录

其它 docker build 的用法

直接用 Gitrepo 进行构建

或许你已经注意到了, docker build 还支持从 URL 构建,比如可以直接从 Gitrepo 中构建:

感谢:漠然提供 Git dockerfile repo

  1. $ docker build https://github.com/mritd/dockerfile.git#:alpine-glibc

  1. ...

  2. Executing ca-certificates-20171114-r0.post-deinstall

  3. Executing busybox-1.27.2-r6.trigger

  4. OK: 11 MiB in 14 packages

  5. Removing intermediate container baf41e622959

  6. ---> aded3329be1b

  7. Step 3/3 : ENV LANG=C.UTF-8

  8. ---> Running in 8c34e0f4d25b

  9. Removing intermediate container 8c34e0f4d25b

  10. ---> c493a5aa5eb7

  11. Successfully built c493a5aa5eb7

这行命令指定了构建所需的 Gitrepo,并且指定默认的 master 分支,构建目录为 /alpine-glibc/,然后 Docker就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。

用给定的 tar 压缩包构建

  1. $ docker build http://server/context.tar.gz

如果所给出的 URL 不是个 Gitrepo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。

从标准输入中读取 Dockerfile 进行构建

  1. $ docker build - < Dockerfile

  1. $ cat Dockerfile | docker build -

如果标准输入传入的是文本文件,则将其视为 Dockerfile,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 COPY 进镜像之类的事情。

从标准输入中读取上下文压缩包进行构建

  1. $ docker build - < context.tar.gz

如果发现标准输入的文件格式是 gzipbzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。

参考:Docker — 从入门到实践

https://www.gitbook.com/download/pdf/book/yeasy/docker_practice

参考:Docker 官网 Get Docker CE for Ubuntu

https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#install-using-the-convenience-script

Contact

  • 作者:鹏磊

  • 出处:http://www.ymq.io

  • Email:admin@souyunku.com

  • 版权归作者所有,转载请注明出处

  • Wechat:关注公众号,搜云库,专注于开发技术的研究与知识分享


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存