Docker 入门实战

Docker基础知识

Docker是什么

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroupnamespace,以及AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

为什么要用Docker

环境配置一直是软件开发的麻烦之一。用户计算机的环境都不相同,同样的软件,换了一台机器可能根本无法运行。环境包括操作系统的设置,各种库和组件的安装。只有它们都正确,软件才能运行。举例来说,安装一个 Python 应用,计算机必须有 Python 引擎,还必须有各种依赖,可能还要配置环境变量。

环境配置如此麻烦,换一台机器,就要重来一次,让人身心俱疲。那么,软件可不可以附带环境安装?也就是说,安装的时候,能否把可运行的原始环境复制过来? Docker就是来解决这个问题的。

Docker 与 虚拟机

这么听起来,Docker与虚拟机好像是一个东西。确实,它们都是带环境安装的不同解决方案,然而,虚拟机有以下几个劣势:

  • 资源占用多

    虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。

  • 冗余步骤多

    虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。

  • 启动慢

    启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。

由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。显然,直接对进程隔离,容器的几大优势就体现出来了:

  • 启动快
  • 资源占用少
  • 体积小

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。

虚拟机与Docker对比:

virtualization

docker

安装Docker

以下以 ubuntu Xenial 16.04 (LTS) 为例,安装Docker社区版(Docker CE),

其它操作系统下的安装可能略有不同,详见Get Docker CE for Ubuntu

首先,如果需要卸载旧版Docker:

1
$ sudo apt-get remove docker docker-engine docker.io containerd runc

在一台新主机上安装Docker时,官方推荐使用Docker仓库来安装,此后可以很方便地从仓库安装和更新Docker。由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。

1
2
3
4
5
6
7
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common

添加Docker官方的GPG秘钥:

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

如果下载失败,可以挂代理,或者直接在我的仓库获取

验证秘钥

1
$ sudo apt-key fingerprint 0EBFCD88

能看到包含9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88的输出就证明正确的安装了Docker CE的秘钥。

添加repo,并更新 apt

1
2
3
4
5
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update

安装Docker时,可以直接选择安装最新版本:

1
$ sudo apt-get install docker-ce

也可以指定版本:

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

如果需要查看可用版本:

1
$ apt-cache madison docker-ce

就是列出版本,版本号等信息。例如,我的输出如下:

1
2
3
docker-ce | 18.03.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu artful/stable amd64 Packages
docker-ce | 17.12.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu artful/stable amd64 Packages
docker-ce | 17.12.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu artful/stable amd64 Packages

Docker Hello world

1
$ sudo docker run hello-world

docker会首先从本地查找hello-world镜像:

1
2
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

当本地不存在这个镜像时,会尝试从远程仓库拉取镜像。

当然,由于某些众所周知的原因,你可能会在拉取镜像的时候会遭遇以下错误:

1
2
3
4
5
6
7
9bb5a5d4561a: Pulling fs layer 
docker: error pulling image configuration: Get
...
...
...
net/http: TLS handshake timeout.
See 'docker run --help'.

解决方法也很简单:

  1. 科学地上网
  2. 修改默认远程镜像仓库

Docker为中国也提供了官方的镜像库:https://registry.docker-cn.com ,但是个人感觉也并不快。国内还有很多镜像库可以使用,例如阿里云,网易云,DaoCloud等。以下以配置阿里云镜像库为例。

要获得阿里云的镜像加速服务,需要注册成为阿里云用户。登录后,在阿里云的管理控制台 -> 容器镜像服务 -> 镜像加速器 可以看到配置文件以及其中的链接。链接为https://[yourcode].mirror.aliyun.com,其中[yourcode]为每个注册用户的专属代码。

配置方法如下:

  • Ubuntu 16.04+、Debian 8+、CentOS 7(其它旧版本可能有差异 )

    1
    $ sudo vim /etc/docker/daemon.json

    写入以下内容:

    1
    2
    3
    {
    "registry-mirrors": ["https://[yourcode].mirror.aliyun.com"]
    }

    重启服务:

    1
    2
    $ sudo systemctl daemon-reload
    $ sudo systemctl restart docker

之后,可以通过sudo docker info查看 Registry Mirrors 信息,就能看到配置好的镜像仓库了。

重新运行hello-world

1
$ sudo docker run hello-world
1
2
3
4
5
6
7
8
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9bb5a5d4561a: Pull complete
Digest: sha256:bbdaf0ed64b665f3061aeab15b946697dd00845161935d9238ed28e8cfc2581c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

此时,已经能够成功从国内镜像库拉取镜像了。

镜像,容器 与 仓库

Docker中最重要的三个基本概念就是镜像(image),容器(container) 与 仓库(Repository)。详细介绍参见Docker基本概念

以刚才运行的hello-world为例。hello-world:latest是一个镜像,查看镜像:

1
2
3
4
$ sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest e38bc07ac18e 32 hours ago 1.85kB
mysql 5.7 5709795eeffa 5 months ago 408MB

命令docker run hello-world实际上是一个创建容器并启动的过程。

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

查看容器的命令如下:

1
2
$ sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

但是,我们什么都没看到。这是因为,已经终止的容器,不会被列出。而hello-world的作用是输出一个 “Hello World”,之后终止容器。因此,此时该容器已经终止。加上-a参数可以查看已经终止的容器:

1
2
3
$ sudo docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
50a8659057c2 hello-world "/hello" 19 minutes ago Exited (0) 19 minutes ago competent_jepsen

可以用docker container rm [Container]来删除一个处于终止状态的容器,也可以用docker container prune清理掉所有处于终止状态的容器。

镜像的更多操作:使用 Docker 镜像。容器的更多操作:操作 Docker 容器

Docker nginx 实战入门

接下来,我们以个人博客为例,用docker+nginx部署自己的个人博客。其中,nginx的作用在这里只是一个静态文件服务器。

获取镜像

想要获取一个镜像,最简单的方法,就是查看是否已经有了满足要求的镜像。命令行查看:

1
2
3
4
5
6
7
8
$ sudo docker search nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 8341 [OK]
jwilder/nginx-proxy Automated Nginx reverse proxy for docker con… 1316 [OK]
richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable of… 545 [OK]
jrcs/letsencrypt-nginx-proxy-companion LetsEncrypt container to use with nginx as p… 348 [OK]
kong Open-source Microservice & API Management la… 174 [OK]
webdevops/php-nginx Nginx with PHP-FPM 99 [OK]

当然,你也可以直接上官方镜像站的网页查看。

事实是,常用镜像,一般来说都会有很多优质镜像已经被搭建好。像Nginx这样的,还有官方(Official)镜像。可以直接拉取官方nginx镜像:

1
sudo docker pull nginx

当然,你也可以自己用Dockerfile构建一个自己的镜像,这个我们以后再说。

首先,假设你已经有了自己博客的所有静态文件(至少有一个index.html

并且写好了nginx的配置文件blog.config,那么就可以开始运行你的第一个容器了。

以上准备的文件,容器的运行都会需要,然而正如上文所说,容器是可以完全与当前宿主环境隔离的,那么,如何让容器访问/拥有它们呢?解决这个问题一般有两种方法:

  1. 将文件放入(拷贝到)容器中。
  2. 挂载到容器中。

这篇文章里,我们暂时只使用挂载这一种方法。

blog.conf放在/home/ace/Documnets/sites-enabled/(这是我的个人目录)下,blog/放在/home/ace/Documnets/下,就可以开始挂载文件并启动容器了:

1
2
3
4
5
6
$ sudo docker run --name myNginx \ 
-d \
-p 8081:8080 \
-v /home/ace/Documents/blog:/static \
-v /home/ace/docker-learning/sites-enabled:/etc/nginx/sites-enabled \
nginx

这一串命令实际上是docker run [FLAGS] nginx

-d是将容器放在后台运行,-p指定端口映射,即将容器的8080端口映射到主机的8081端口,也就是说,我们在主机中访问8081端口的时候,就可以访问到容器内部的8080端口。这里使用8081是因为我本机的8080端口已经在使用了。

-v表示挂载,可以简单的理解为:当容器中的nginx运行时,如果要访问到/static目录时,实际访问到的是宿主机(即你的电脑)的/home/ace/Documents/blog目录。同理,sites-enabled目录也形成了映射。一般来说,官方建议挂载目录而不是挂载文件。另外,熟悉nginx的话会知道,最官方/规范的做法是将这个配置文件放在/etc/nginx/sites-available/下,再建立软链接。

容器开始运行后,在浏览器中访问localhost:8081,就可以看到博客了:

Trouble-shooting

遇到错误中带有提示TLS或者HTTP/HTTPS时,配置了国内镜像库能解决80%的问题,科学的上网能解决90%的问题。

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,为了避免每次输入命令都需要sudo,可以选择使用 docker 的用户加入 docker 用户组:

1
$ sudo usermod -aG docker $USER

本文为博主原创文章,转载请注明出处。