Docker新手入门:基本用法

Docker新手入门:基本用法


1.Docker简介

1.1 第一本Docker书

工做中不断碰到Docker,今天终于算是正式开始学习了。在挑选系统学习Docker以及虚拟化技术的书籍时还碰到了很多麻烦,主要就是没有特别经典的书!Docker的《初版Docker书》和《Docker技术入门与实战》广泛评价不高,而《Docker开发实践》和《Dockeru源码分析》又是2015年最近才出的,评价不是不少。综合看了下,最终仍是选择了《Docker开发实践》,如下都主要以这本书做为学习资料。前端

1.2 Docker是什么?

《Docker开发实践》中讲了个故事:20世纪60年代之前的海运,货物都放置在一块儿,很容易挤压受损。同时,不一样的运输方式之间的转运也很麻烦,例如从码头和火车汽车转运卸货时。不一样货物和不一样交通工具之间的组合是一个巨大的二维矩阵。海运界最后达成了一致,制定了国际标准集装箱来解决这个棘手的问题。全部货物都打包进集装箱互相隔离,全部交通工具都经过集装箱转运,极大地提供了运输的安全性和效率。linux

在软件开发中咱们也常常碰到一样的问题,使用了各类各样技术框架的应用程序,从前端静态网站到后端数据库,从PHP到Java,以及多种多样的部署环境,从测试服务器到线上环境,从虚拟机到公有云等等。Docker,正是这个集装箱,而Docker的logo也的确是个集装箱。nginx

1.3 Docker与容器和虚拟机

很天然地咱们会问,Docker跟虚拟机有什么区别啊?这个问题能够拆成两部分。由于Docker并非什么彻底首创的技术,而是属于很早便有了的容器技术,因此第一个问题就是容器与虚拟机的区别?同属于容器技术,Docker的兄弟姐妹还有Solaris Zones、BSD jails、LXC等。但Docker如今这么火,天然有它的独到之处,因此第二个问题就是Docker与其余容器的区别?git

关于第一个问题比较简单,容器是一种轻量级的虚拟技术。它不像虚拟机那样具备一套完整的CPU、内存和磁盘,对操做系统有绝对的权限。容器和宿主主机共享内核,全部容器共享操做系统,在一台物理机上能够运行成百上千的容器。第二个问题稍麻烦一些,与LXC相比,Docker对配置进行了抽象,使应用在任何平台上的运行环境都一致。同时提供了版本控制、镜像托管等相似Git的现代化设施和生态圈。github

整体来看,Docker的应用场景有:sql

  • 加速本地开发:快速搭建好开发环境和运行环境。
  • 自动打包和部署应用。
  • 建立轻量级的私有Paas环境。
  • 自动化测试和持续集成。
  • 建立安全沙盒。

2.Docker安装与32位问题

2.1 安装Docker

Docker对Linux环境有两个要求,一是64位系统,二是内核在3.8以上。而我使用的是Linux Mint 17的32位版,因此下载了源码包准备编译安装。正愁找不到编译安装的资料时,发现Ubuntu软件库提供了已经编译好的Docker 32位版,真是太好了!再看一下个人内核版本是3.13,也符合要求,因而直接用apt安装。docker

cdai@dell ~ $ uname -a
Linux dell 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:30:01 UTC 2014 i686 i686 i686 GNU/Linux

cdai@dell ~ $ apt-cache search docker
docker.io - Linux container runtime
kdocker - lets you dock any application into the system tray
vim-syntax-docker - Docker container engine - Vim highlighting syntax files

cdai@dell ~ $ sudo apt-get install docker.io
cdai@dell ~ $ docker -v
Docker version 1.0.1, build 990021a

2.2 32位版镜像

尽管Docker能用了,可是官方Docker Hub中的镜像都是为64位系统准备的,下载这些镜像后建立启动容器时会报”finalize namespace drop capabilities operation not permitted”的错误。因此咱们能够用官方提供的Dockerfile构建出32位版本的镜像,才能在32位系统上使用。(镜像构建的具体讲解请参见第3.4节)数据库

以构建32位版的Ubuntu为例,执行官方GitHub上提供的Shell脚本便可。通过漫长的等待后,就能看到32位的Ubuntu镜像已经成功安装到咱们本地了,官方的脚本果真仍是挺靠谱的。bootstrap

cdai ~ $ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              VIRTUAL SIZE
32bit/ubuntu        14.04               c062cc00654e        About a minute ago   295.3 MB

注意:这里为了在个人老本子上学习研究Docker而作了workaround,真正应用时固然仍是必定要在64位机器上使用Docker的。ubuntu


3.入门实战

3.1 核心概念

在开始使用Docker以前,首先要了解Docker中的概念和它们之间的关系,不然直接上手可能会搞得一头雾水。Docker中最重要的三个概念就是:镜像、容器、库。

  • 镜像:是一个包含了应用程序和其运行时依赖环境的只读文件。
  • 容器:它是构建容器的模板,经过一个镜像咱们能够构造出不少相互独立但运行环境同样的容器。
  • :Docker提供了Hub来保存公有或私有的镜像,也容许第三方搭建。

下面就是典型的Docker工做流,从这张图中能清晰地理解这三个重要概念之间的关系。本节接下来就根据这个Workflow逐一介绍经常使用的操做。

docker-workflow

3.2 搜索下载镜像

首先用docker search [keyword]命令查看Docker Hub上都有哪些镜像能够下载,search后能够用通配符表示关键字:

cdai ~ $ docker search ubuntu
NAME                           DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
ubuntu                         Ubuntu is a Debian-based Linux operating s...   2179      [OK]       
ubuntu-upstart                 Upstart is an event-based replacement for ...   31        [OK]       
torusware/speedus-ubuntu       Always updated official Ubuntu docker imag...   25                   [OK]
tleyden5iwx/ubuntu-cuda        Ubuntu 14.04 with CUDA drivers pre-installed    17                   [OK]
ubuntu-debootstrap             debootstrap --variant=minbase --components...   12        [OK]       
neurodebian                    NeuroDebian provides neuroscience research...   11        [OK]       
    ...

接下来用docker pull [repository/url:tag]命令下载镜像。由于从官方Docker Hub下载很是慢,因此这里从国内的镜像站http://dockerpool.com/下载,速度很是快。(注:后面会讲到用docker run命令建立容器,其实若是镜像不存在Docker会自动去下载,这里为了学习pull命令因此手动下载镜像)

cdai ~ $ docker pull dl.dockerpool.com:5000/ubuntu:14.04
cdai ~ $ docker pull dl.dockerpool.com:5000/centos:latest

下载完成后,就能够用docker images查看本地都有哪些镜像。这里的REPOSITORY列可能有三种类型:

  • [namespace/ubuntu]:当你在Docker Hub上注册帐户时,帐户名就自动成为你的namespace,它是用来区分不一样用户的镜像的。
  • [ubuntu]:这种只有仓库名的能够认为它属于顶级namespace,这种仓库只用于官方的镜像。
  • [dl.dockerpool.com:5000/ubuntu]:URL路径表示镜像是放置在第三方搭建的Hub上。
cdai ~ $ docker images
REPOSITORY                      TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
dl.dockerpool.com:5000/ubuntu   14.04               5506de2b643b        10 months ago       199.3 MB
dl.dockerpool.com:5000/centos   latest              87e5b6b3ccc1        11 months ago       224 MB

若是想要查看镜像的详细信息,能够用docker inspect [image-id]命令查看。(注:下面容器一节会看到,这个命令也可以用来查看容器的详细信息)

cdai ~ $ docker inspect 5506de2b643b
[{
    "Architecture": "amd64",
    "Author": "",
    "Comment": "",
    "Config": {
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Cmd": [
            "/bin/bash"
        ],
        ...
    },
    "Container": "201ae89099c20e577dd9c60cb9199c2dac0688d49efa676bf3eeb859666294bd",
    "ContainerConfig": {
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        ...
    },
    "Created": "2014-10-23T23:53:59.03271073Z",
    "DockerVersion": "1.3.0",
    "Id": "5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5",
    "Os": "linux",
    "Parent": "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723",
    "Size": 0
}]

3.3 建立启动容器

了解了镜像的基本操做后,咱们就能够建立容器了。首先用docker create建立容器或者用docker run [repository:tag]建立并运行容器。容器能够分为两种类型:

  • 交互型容器:前台运行,能够经过控制台与容器交互。若是建立该容器的终端被关闭,则容器就变为中止状态。此外,在容器控制台中输入exit或者经过docker stopdocker kill也能终止容器。
  • 后台型容器:后台运行,建立启动以后就与终端无关了,须要用docker stopdocker kill来终止。

说明:由于个人老本子是32位的,即使从第三方Hub安装了镜像也都是64位的没法使用,因此这里的例子都使用第一节中手动构建出的32位Ubuntu镜像。

首先咱们建立运行一个交互型容器试试,在容器的控制台里简单的输出个”Hello,Docker”。怎么样?Docker容器很是轻量级,启动很是快吧!用docker ps能够查看正在运行的容器,用docker ps -a查看全部容器,包括未启动的容器。(-l和-n=x能列出最后建立的一个或x个容器)

cdai ~ $ docker run -i -t 32bit/ubuntu:14.04 /bin/bash
root@328aa6305d82:/# echo "Hello, Docker!"
Hello, Docker!
root@328aa6305d82:/# exit
exit

cdai ~ $ docker ps -a
CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS                     PORTS               NAMES
328aa6305d82        32bit/ubuntu:14.04   /bin/bash           24 seconds ago      Exited (0) 4 seconds ago                       stupefied_curie

如今再建立运行一个后台型容器。能够用restart参数设置容器异常退出时自动重启:”always”无论什么返回码都尝试重启容器;”on-failure:x”则只在返回码非0时才会重启,并尝试x次。最后能够接上一个容器启动后执行的命令。这里为了作一个例子学习,因此用一个循环每隔5秒输出”hello world”。(Docker 1.3后提供了docker exec用于在容器运行以后中途启动另外一个程序)

cdai ~ $ docker run -d 32bit/ubuntu:14.04 /bin/bash -c "while true; do echo hello world; sleep 5; done"
5c40590cc23cfa02bd6c0220da77b601973f01f8bc430baca22315ef68d81e44
cdai ~ $ docker ps
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS              PORTS               NAMES
5c40590cc23c        32bit/ubuntu:14.04   /bin/bash -c while    5 seconds ago       Up 5 seconds                            evil_bartik

如今容器已经在后台运行起来了,没有控制台咱们怎样能查看容器的状态呢?Docker提供了一些很实用的容器内操做命令,例如docker logs [container-name]可以查看容器内标准输出流中的内容,docker top [container-name]查看容器中的全部进程。下面就用这两个命令查看一下刚才启动的容器运行得怎么样了?注意:启动容器时若是没用name参数指定容器名称的话,Docker会自动生成一个,下面的命令都用这个名称做为参数。

cdai ~ $ docker logs -f evil_bartik
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
^C
cdai ~ $ docker top evil_bartik
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                26803               1700                0                   21:54               ?                   00:00:00            /bin/bash -c while true; do echo hello world; sleep 5; done
root                26859               26803               0                   21:55               ?                   00:00:00            sleep 5

能够看到咱们的容器运行良好,循环体在不断输出”hello world”到输出流中。以前学习镜像说过docker inspect也能够用于容器,如今就用它查看一下容器的详细信息。从输出能够清晰地看到启动命令、环境参数、网络等信息。

cdai ~ $ docker inspect evil_bartik
[{
    "Args": [
        "-c",
        "while true; do echo hello world; sleep 5; done"
    ],
    "Config": {
        "AttachStderr": false,
        "AttachStdin": false,
        "AttachStdout": false,
        "Hostname": "5c40590cc23c",
        "Image": "32bit/ubuntu:14.04",
        ...
    },
    "Created": "2015-09-03T13:54:03.389202827Z",
    "Driver": "aufs",
    ...
    "MountLabel": "",
    "Name": "/evil_bartik",
    "NetworkSettings": {
        "Bridge": "docker0",
        "Gateway": "172.17.42.1",
        "IPAddress": "172.17.0.5",
        "IPPrefixLen": 16,
        "PortMapping": null,
        "Ports": {}
    },
    ...
}]

容器学习的差很少了,如今就把它停下来了吧。以前说过,对于后台型容器有docker stop [container-name]docker kill [container-name]两种方法。那它们有什么区别呢?前者会给容器内的进程发送SIGTERM信号,默认行为是容器退出,固然容器内的程序也能够捕获该信号后自行处理。然后者会给容器内的进程发送SIGKILL信号,致使容器直接退出。

cdai ~ $ docker stop evil_bartik
evil_bartik
cdai ~ $ docker ps -a
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS                      PORTS               NAMES
5c40590cc23c        32bit/ubuntu:14.04   /bin/bash -c while    3 minutes ago       Exited (-1) 5 seconds ago                       evil_bartik         
328aa6305d82        32bit/ubuntu:14.04   /bin/bash              11 minutes ago      Exited (0) 11 minutes ago                       stupefied_curie

3.4 制做上传镜像

本地镜像的制做有使用commit命令和编写Dockerfile两种方式。下面就先分别介绍这两种制做方式,而后学习一下如何把咱们本身制做的本地镜像上传到Docker Hub上。

3.4.1 用commit制做镜像

首先启动咱们以前建立的交互型容器stupefied_curie。启动成功后会发现咱们并无进入容器的控制台,这时就要使用以前没有介绍的一个容器内使用的命令,attach命令,帮咱们从新进入一个已启动容器的控制台。(注意:执行attach后,要按一次回车才会出现容器的控制台界面。并且后台型容器是没法attach的)

cdai ~ $ docker ps -a
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS                    PORTS               NAMES
328aa6305d82        32bit/ubuntu:14.04   /bin/bash              10 hours ago        Exited (0) 10 hours ago                       stupefied_curie

attach上以后咱们就能够在容器内操做了,这里咱们安装一个Sqlite数据库并保存一个文本文件,准备好要制做成本地镜像的容器。本觉得一切会很顺利,结果却碰到了容器内没法上网的问题,由于默认容器与宿主主机是桥接的关系须要配置一些DNS等设置。上网查了一下最简单的方法就是改一下网络模式,让容器使用与宿主主机同样的网络栈。这里从新建立一个交互型容器,网络模式使用host模式。

cdai ~ $ docker run -i -t --net="host" 32bit/ubuntu:14.04 /bin/bash
root@cdai:/# apt-get install sqlite3
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Suggested packages:
  sqlite3-doc
The following NEW packages will be installed:
  sqlite3
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 28.3 kB of archives.
After this operation, 161 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main sqlite3 i386 3.8.2-1ubuntu2.1 [28.3 kB]
Fetched 28.3 kB in 2s (10.8 kB/s)                   
Selecting previously unselected package sqlite3.
(Reading database ... 11535 files and directories currently installed.)
Preparing to unpack .../sqlite3_3.8.2-1ubuntu2.1_i386.deb ...
Unpacking sqlite3 (3.8.2-1ubuntu2.1) ...
Setting up sqlite3 (3.8.2-1ubuntu2.1) ...
root@cdai:/# echo "test docker commit" >> hellodocker
root@cdai:/# exit
exit

如今就能够用docker commit [container-id]命令将前面准备好的容器制做成镜像了!(为何有的命令用ID有的用name呢?)执行完成后,就能查咱们的镜像已安装到本地库了。这里还发现了一个问题,就是基于32位镜像建立的容器,再制做成镜像好像又是64位了,启动时又会报”finalize namespace drop capabilities operation not permitted”错误,:(

cdai ~ $ docker ps -a
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS                     PORTS               NAMES
1718e35463ff        32bit/ubuntu:14.04   /bin/bash              53 seconds ago      Exited (0) 8 seconds ago                       boring_babbage      

cdai ~ $ docker commit --author="cdai" 1718e35463ff cdai/sqlite3:v1
e10622157a5df7c82bdf148bda021e0fe312c153852f7a6c765c216d4d636ecb
cdai ~ $ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
cdai/sqlite3        v1                  e10622157a5d        6 seconds ago       321.8 MB
32bit/ubuntu        14.04               c062cc00654e        11 hours ago        295.3 MB

3.4.2 用Dockerfile制做镜像

上面咱们在容器的控制台上手动执行一些操做,而Dockerfile是一种更加透明而且可重复的制做方式,由于咱们不是手动执行操做,而是将全部操做用Docker提供的命令和语法编写到Dockerfile中。命令名所有大写,经常使用的Dockerfile命令有:

  • FROM:指定扩展自哪一个父级镜像。
  • RUN:执行命令修改镜像。例如RUN apt-get update和RUN [“apt-get”, “update”]。前者在/bin/sh中执行命令,后者直接使用系统调用exec执行。
  • EXPOSE:指明容器内进程对外开放的端口。也能够在容器启动时用-p参数开放一些在Dockerfile里没有列出的端口。
  • ADD:添加宿主主机文件、文件夹或URL指定资源到镜像中。
  • ENV:设置容器运行的环境变量。
  • USER:为容器的运行以及Dockerfile后面的命令指定用户。

其余命令还有:MAINTAINER声明做者信息、WORKDIR指定工做目录(最后一个会做为容器启动后的工做目录)、VOLUME挂载文件、CMD和ENTRYPOINT指定容器启动后执行的命令、ONBUILD指定一些命令在当前镜像构建时不会执行,而是在子镜像构建时触发。

了解了这些经常使用的命令,下面就能够开始编写Dockerfile了。注意:Dockerfile文件名默认就叫”Dockerfile”,不然执行build命令时Docker会找不到。编写好后,执行docker build .就能够开始构建了,每一条Dockerfile的命令都至关于构建出一个临时镜像,最后一步会生成最终的目标镜像。

cdai $ ls
-rw-r--r--  1 root root   17 Sep  4 09:08 abc.txt
-rw-r--r--  1 root root  188 Sep  4 09:08 Dockerfile
cdai $ cat Dockerfile 
FROM 32bit/ubuntu:14.04
MAINTAINER cdai "XXX@163.com"
USER root
RUN apt-get install -y nginx
EXPOSE 80 8080
RUN touch test.txt
ADD abc.txt .
ENTRYPOINT ["ls"]
CMD ["-a", "-l"]

cdai $ docker build -t cdai/nginx:v1 .
Sending build context to Docker daemon 3.584 kB
Sending build context to Docker daemon 
Step 0 : FROM 32bit/ubuntu:14.04
 ---> c062cc00654e
Step 1 : MAINTAINER cdai "dc_726@163.com"
 ---> Running in 53ece79679ed
 ---> 979f81286bbe
Removing intermediate container 53ece79679ed
Step 2 : USER root
 ---> Running in f5d29e4895e5
 ---> 4965bbb8d8cd
Removing intermediate container f5d29e4895e5
Step 3 : RUN apt-get install -y nginx
 ---> Running in d4e46baff94a
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  fontconfig-config fonts-dejavu-core geoip-database libfontconfig1
  libfreetype6 libgd3 libgeoip1 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5
  libvpx1 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxml2 libxpm4
  libxslt1.1 nginx-common nginx-core sgml-base xml-core
Suggested packages:
  libgd-tools geoip-bin fcgiwrap nginx-doc sgml-base-doc debhelper
  ...
Setting up nginx (1.4.6-1ubuntu3.3) ...
 ---> 73f5d909ac29
Removing intermediate container d4e46baff94a
Step 4 : EXPOSE 80 8080
 ---> Running in 4333d2f3c58c
 ---> b693316fb774
Removing intermediate container 4333d2f3c58c
Step 5 : RUN touch test.txt
 ---> Running in 815dfbfb2005
  ...

3.4.3 上传镜像到Hub

首先用docker login命令输入咱们在Docker Hub的登陆信息,Docker会将其保存到~/.dockercfg中。

cdai $ docker login
Username: cdai
Password: 
Email: XXX@163.com
Login Succeeded
cdai $ cat ~/.dockercfg 
{"https://index.docker.io/v1/":{"auth":"XXXXaToxXXXXXXlEYiE=","email":"XXX@163.com"}}

cdai $ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
cdai/sqlite3        v1                  e10622157a5d        37 minutes ago      321.8 MB
32bit/ubuntu        14.04               c062cc00654e        11 hours ago        295.3 MB

cdai $ docker push cdai/sqlite3:v1
The push refers to a repository [cdai/sqlite3] (len: 1)
Sending image list
Pushing repository cdai/sqlite3 (1 tags)
c062cc00654e: Buffering to disk 
    ...

4.附:Docker相关技术

  • 容器隔离:采用libcontainer取代了LXC做为默认容器。经过内核的pid、net、ipc、mnt和uts等命名空间实现对进程、网络、消息、文件系统和主机名的隔离。
  • 资源调配:经过cgroups控制资源的度量和分配。
  • 可移植性:利用AUFS实现对容器的快速更新。AUFS具备层的概念,每次修改都是在已有的只写层进行增量修改,修改内容造成新的文件层而不影响原有的层。
  • 安全性:经过命名空间的隔离和cgroups审计,并配合一系列工具如SELinux等来保证安全。
相关文章
相关标签/搜索