Iawen's Blog

我喜欢这样自由的随手涂鸦,因为我喜欢风......

0 3 Docker 支持 Windows/Linux/Mac/AWS/Azure 多种平台的安装,其中 Windows 需要 Win10+,Mac 需要 EI Captain+。Docker 是一个 C/S 架构的服务,安装好 docker 之后需要启动 docker 软件后才能使用 docker 命令。

1. Docker 基础

Docker 主要有 Dockerfile, Image, Container, Repository 四个基本概念。通过 Dockerfile 我们可以生成 Docker Image(镜像)。自己制作的镜像可以上传到 Docker hub 平台,也可以从平台上拉去我们需要的镜像。当镜像拉到本地之后,我们就可以实例化这个镜像形成一个 Container(实例) 了。一个简单的镜像启动的命令是:

$ docker run [组织名称]/<镜像名称>:[镜像标签]

其中除了镜像名称,其它的都是可选参数。组织名称不填默认为library,镜像标签不填则默认为latest。例如经典的启动一个 Hello World 镜像的过程如下: 1

可以看到当我实例化hello-world这个镜像的时候,docker 发现本地没有这个镜像会先去 Docker hub 远端拉取镜像,如刚才说的,默认是latest标签。拉取后就会实例化执行入口命令了。我们除了可以使用 Docker hub 查找我们需要的镜像之外,也可以使用docker search命令来查找。

1.1 安装

Docker 软件包已经包括在默认的 CentOS-Extras 软件源里。因此想要安装 docker,只需要运行下面的 yum 命令: yum install docker

1.2 启动 Docker 服务

安装完成后,使用下面的命令来启动 docker 服务,并将其设置为开机启动:

service docker start    #systemctl start docker.service
chkconfig docker on     #systemctl enable docker.service

下面我们就来看看如何运行一个 Nginx 容器实例:

$ docker run
 -d
 --rm
 -p 8080:80 
 -v "$PWD/workspace":/var/www/hello.world
 -v "$PWD/hello.world.conf":/etc/nginx/conf.d/hello.world.conf
 nginx

使用docker run命令就能启动一个实例了,其中-p表示将本机的 8080 端口映射到镜像实例内的 80 端口,而-v表示将本地的$PWD/workspace文件夹映射到镜像实例里的/var/www/hello.world文件夹,后面的同理。最后再指定一下镜像名称,就能完成一次 Nginx 实例的启动了。此时访问http://hello.world:8080即可看到效果。

注:千万不要在容器实例中存储内容,实例销毁时实例内的所有内容都会被销毁,下次启动的时候又是全新的实例,内容不会保存下来。如果需要存储服务需要使用挂载卷或者外部存储服务。

Docker 使用 /etc/docker/daemon.json 来配置 Daemon 在该配置文件中加入(没有该文件的话,请先创建一个):

{
    "registry-mirrors":["https://docker.mirrors.ustc.edu.cn"]
}
[root@iawen ~]# docker pull centos
Using default tag: latest
Trying to pull repository docker.io/library/centos ... 
latest: Pulling from docker.io/library/centos
729ec3a6ada3: Pull complete 
Digest: sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9
Status: Downloaded newer image for docker.io/centos:latest

# 确认 CentOS 镜像已经被获取:
[root@iawen ~]# docker images centos
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/centos    latest              0f3e07c0138f        4 weeks ago         220 MB

1.3 运行一个 Docker 容器

docker run -i -t centos /bin/bash
# 我们可以看到,CentOS 容器已经被启动,并且我们得到了 bash 提示符。
# 在 docker 命令中我们使用了 “-i 捕获标准输入输出”和 “-t 分配一个终端或控制台”选项。若要断开与容器的连接,输入 exit。

[root@4943a5d6264e /]# cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core) 
[root@4943a5d6264e /]# exit
exit
[root@iawen ~]# docker search ubuntu
[root@iawen ~]# docker search fedora

1.4 显示当前正在运行容器的列表

[root@iawen ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
743845f56051        centos              "/bin/bash"         11 seconds ago      Up 10 seconds                           flamboyant_shirley

2. Dockerfile

Dockerfile 是 Docker 比较重要的概念。它是 Docker 创建镜像的核心,它的出现给 Docker 提供了两大好处:

  • 文本化的镜像生成操作让其方便版本管理和自动化部署
  • 每条命令对应镜像的一层,细化操作后保证其可增量更新,复用镜像块,减小镜像体积 2

Dockerfile 的一些编写规则主要如下: 使用#来注释 FROM 指令告诉 Docker 使用哪个镜像作为基础 RUN 开头的指令会在创建中运行,比如安装一个软件包 COPY 指令将文件复制进镜像中 WORKDIR 指定工作目录 CMD/ENTRYPOINT 容器启动执行命令 RUN 和 CMD/ENTRYPOINT 都是执行命令,区别在于 RUN 是在镜像构建过程中执行的,而 CMD/ENTRYPOINT 是在镜像生成实例的时候执行的,类似于 C/C++ 语言的头文件的正常代码的区别。而且后者在一个 Dockerfile 文件中只能有一个存在。CMD/ENTRYPOINT 的区别除了在写法上有区别之外,还有在docker run命令后增加 CMD 参数的情况下有区别(CMD会被复写)。一般建议使用 ENTRYPOINT 会更方便点。一个简单的 Node 命令行脚本的 Dockerfile 文件如下:

FROM mhart/alpine-node:8.9.3 LABEL maintainer="lizheming <i@imnerd.org>" 
    org.label-schema.name="Drone Wechat Notification" 
    org.label-schema.vendor="lizheming" 
    org.label-schema.schema-version="1.1.0"
WORKDIR /wechat
COPY package.json /wechat/package.json
RUN npm install --production --registry=https://registry.npm.taobao.org
COPY index.js /wechat/index.js
ENTRYPOINT [ "node", "/wechat/index.js" ]

这里我认为依赖是比较固定的,没有代码修改那么频繁,所以将其提前了。最终保证了所以越稳定的变化的命令至于上层,保证了每层打包出来的 Layer 能够尽可能的复用,而不会徒增镜像的大小。最后我们使用如下命令就可以完成一个 Docker 镜像的构建了:

$ docker build lizheming/drone-wechat:latest

参数和docker run是一样的。构建完成之后就可以开心的 push 到 Docker hub 上啦~

3. Docker Compose

以上我们说了下如何启动一个服务,但是我们都明白一个完整的项目肯定是不止依赖一个服务的,而 Docker 镜像的 ENTRYPOINT 只能设置一个,所以难道我们要使用docker run命令手动创建 N 个容器实例吗?为了解决这个问题,Docker Compose 就瞬时出现了。Docker Compose 是一款容器编排程序,使用 YAML 配置的形式将你需要启动的容器管理起来,免去我们需要多次执行docker run命令的烦恼。

Docker Compose 是使用 Python 开发的,它的安装非常的简单,直接pip install docker-compose就好了。安装完成之后分别使用up和stop命令可以启动和停止服务。一个简单的 docker-compose.yaml 配置文件大概如下:

version: "2"
services:
    nginx:
        depends_on:
            - "php"
        image: "nginx:latest"
        volumes:
            - "$PWD/src/docker/conf:/etc/nginx/conf.d"
            - "$PWD:/home/q/system/m_look_360_cn"
        ports:
            - "8082:80"
        container_name: "m.look.360.cn-nginx"
     php:
        image: "lizheming/php-fpm-yaf"
        volumes:
            - "$PWD:/home/q/system/m_look_360_cn"
        container_name: "m.look.360.cn-php"

Docker Compose 的另外一个好处就是能够帮我们处理容器的依赖关系,在每个容器中会将容器的 IP 和服务的名称使用 hosts 的方式绑定,这样我们就能在容器中直接使用服务名称来接入对应的容器了。例如下面这个 Nginx 配置中的php:9000就是利用了这个原理。

server {
    listen 80;
    server_name dev.m.look.360.cn;
    charset utf-8;
    root /home/q/system/m_look_360_cn/public;
    index index.html index.htm index.php;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root html;
    }
    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ .php$ {
        fastcgi_pass php:9000;
        #fastcgi_pass unix:/tmp/fcgi.sock;
        fastcgi_index index.php;
    }
}

4. Docker 相关

基于 Docker 容器虚拟化技术除了以上说的解决部署环境之外,还有一些其它的优点,例如:

  • 基于 Docker 的 CI 持续集成和 CD 持续支付
  • 基于 Kubernetes, Docker Swarm 的集群弹性扩容和缩容
  • CI/CD 对于现在的敏捷开发是非常重要的,自动化任务帮助我们节省很多不必要的开发时间浪费,具体可查看我之间的文章《基于Docker的CI工具》④。而 k8s 和 Docker Swarm 带来的弹性扩容和缩容让业务不在为流量问题而头疼。通过监控报警设置当出现峰值的时候自动扩容抗压,当出现低谷的时候自动去除多余的容器来节省成本,同时也将多余的资源给其它服务使用。

5. Docker命令

命令 说明
docker pull name[:tag] 从网络上下载镜像, 如docker pull ubuntu[:latest]
docker run -t -i ubuntu /bin/bash 使用镜像,如(利用镜像创建一个容器,在其中运行bash应用)
-t 让Docker分配一个伪终端并绑定到容器的标准输入上
-i 让容器的标准输入保持的打开
-d 让Docker容器在后台以守护态(Daemonized)形式守护
docker images 列出本地主机上已有的镜像
docker tag 为本地镜像添加新的标签,如:docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest
docker inspect 返回一个JSON格式的消息,如果只要其中一项内容时,可以使用-f参数辷指定。在指定镜像ID的时候,通常使用该ID的前若干个字符组成的可区分字串来替代完整的ID。
docker search TERM 可以搜索远端仓库中共享的镜像,默认搜索Docker Hub官方仓库中的镜像。选项:
–automated=false 仅显示自动创建的镜像
–no-trunc=false 输出信息不截断显示
-s, –starts=0 抒写仅显示评价为指定星级以上的镜像
docker rmi IMAGE [IMAGE …] 删除镜像,IMAGE可以为标签或ID, 当有该镜像创建的容器存在时,镜像文件默认是无法被删除的!
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] 基于已经镜像的容器创建, 主要选项包括:
-a, –author=”” 作者信息
-m, –message=”” 提交消息
-p, –pause=true 提交时暂停容器运行
docker stop 停止容器运行, docker stop [-t|--time[=10]] 容器ID
docker attach
docker exec -ti ID /bin/bash
进入容器
docker rm [OPTIONS] CONTAINER [CONTAINER …] 删除容器:
-f, –force=false 强制终止并删除
-l, –link=false 删除容器的连接
-v, –volumes=false 删除容器挂载的数据卷
docker export CONTAINER 导出/导入容器, 如: cat test_for_run.tar | docker import - test/ubuntu:v1.0

备注:

  • 基于本地模板导入(推荐使用OpenVZ提供的模板:http://openvz.org):cat ubuntu-14.0.04-x86_64-minimal.tar.gz | docket import - ubuntu:14.04
  • 启动容器有两种方式:
    • 1、基于镜像新建一个容器并启动
    • 2、将在终止状态的容器启动