对开发和管理人员来说,docker是一个用容器开发、发布、运行应用程序的平台。Docker主要包含两个概念,镜像(Image)、容器(container)、Docker Registry:

  • 镜像 ,镜像是一个可执行的package,包含程序运行需要的一切,如代码、库(libraries)、环境变量、配置文件等等

  • 容器,容器是镜像的实例,可以通过docker ps查看当前运行的所有容器进程。

  • Docker Registry,镜像的集中式存储管理服务,一个Docker Registry包含多个仓库( Repository ) , 每个仓库可以包含多个标签( Tag ) ,每个标签对应一个镜像。

Docker架构图:

架构

镜像和虚拟机

容器直接运行在linux上,并且和其它容器共享主机的内核,所以他比其它执行方式实用更少的内存,从而使得它更加轻量级。

而虚拟机运行了一个完整的用户操作系统,通过虚拟机虚拟访问主机资源,通常虚拟机提供环境需要的资源很多。

Docker版本:

  • CE
  • EE

Docker CE安装

清除历史版本

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

设置仓库

  1. 安装需要的package

    $ sudo yum install -y yum-utils \
      device-mapper-persistent-data \
      lvm2
    
  2. 设置稳定的仓库

    $ sudo yum-config-manager \
        --add-repo \
        https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repos
    

    或者

    $ sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    

因为该系统中我将/bin/python指向了python3.x的版本,所以执行时遇到了错误:

File "/usr/bin/yum-config-manager", line 135
    except yum.Errors.RepoError, e:
                                ^

解决版本就是修改/usr/bin/yum-config-manager,将第一行指定的python作修改如下:

#!/usr/bin/python2.7 -tt

启用nightly和test repositories(可选)

#启用
$ sudo yum-config-manager --enable docker-ce-nightly
$ sudo yum-config-manager --enable docker-ce-test
# 禁用
$ sudo yum-config-manager --disable docker-ce-nightly

安装CE

sudo yum install docker-ce

或者查看可选的版本进行安装:

$ yum list docker-ce --showduplicates | sort -r

docker-ce.x86_64  3:18.09.1-3.el7                     docker-ce-stable
docker-ce.x86_64  3:18.09.0-3.el7                     docker-ce-stable
docker-ce.x86_64  18.06.1.ce-3.el7                    docker-ce-stable
docker-ce.x86_64  18.06.0.ce-3.el7                    docker-ce-stable

$ sudo yum install docker-ce-<VERSION STRING>

启动docker

$ sudo systemctl start docker

镜像源的修改

# vi /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://*****.mirror.aliyuncs.com",
    "https://reg-mirror.qiniu.com/"
  ]
}

一些基础命令

## List Docker CLI commands
docker
docker container --help

## Display Docker version and info
docker --version
docker version
docker info

## Execute Docker image
docker run hello-world

## List Docker images
docker image ls

## List Docker containers (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq

## 批量删除已经停用的容器
docker container rm `docker ps -a|grep Exited|awk '{print $1}'`

容器(Containers)

在以前,我们要写一个Python的应用,我们第一件事情就是在机器上安装Python的运行环境,但是这会导致一个问题,你的环境要和你应用预期的一直,而且生产环境也是这样的。

但是如果有了Docker,你就可以将Python运行环境移植成一个镜像。然后,你的构建可以包含基础的python镜像和应用代码。

我们可以通过Dockerfile去定义这样轻便的镜像。

Dockerfile定义了容器内的环境发生了什么,在这个环境里网络接口、磁盘驱动器等资源的访问都将会被虚拟化,所以需要将端口映射到容器外部,并指定要拷贝至容器内访问的文件。这样通过Dockerfile构建的应用无论在哪运行都会和预期是一致的。

# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

开发完Dockerfile之后,下面就是构建应用:

$ docker build --tag=friendlyhello<:version> .

这样之后我们构建的镜像就会存在与我们本地的Docker注册表中。

运行容器:

// 终端运行
$ docker run -p 4000:80 friendlyhello
// 后台运行
$ docker run -d -p 4000:80 friendlyhello

查看后台运行的容器:

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED
1fa4ab2cf395        friendlyhello       "python app.py"     28 seconds ago

停止指定的容器:

$ docker container stop 1fa4ab2cf395

将本地的镜像和注册表中的仓库作关联:

$ docker tag image username/repository:tag
// 示例:
$ docker tag friendlyhello gordon/get-started:part2

发布镜像:

$ docker push username/repository:tag

从远程仓库拉取尽量并运行:

$ docker run -p 4000:80 username/repository:tag
//例如:
$ docker run -p 4000:80 gordon/get-started:part2

服务(Services)

在分布式应用中,应用的不同部分称为服务,例如,有一个视频分享网站,它可能包含在数据库中储应用数据的服务、用户在后台上传视频的转码服务、前段的服务等等。

服务就是生产中容器,一个服务只运行一个镜像,但是它定义了镜像的运行方式,如应该用什么端口、服务达到所需的生产能力要多少容器的副本等等。

在Docker平台上,可以通过编写docker-compose.yml做到定义、运行、伸缩服务。

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

该文件告诉Docker:

  • username/repo:tag拉取镜像
  • 运行5个该镜像的实例命名为web,并限制每个实例最多只占用所有CPU资源的10%,以及50MB内存。
  • 如果有一个失败,则立即重启容器。
  • 映射容器内的80端口到主机的4000端口
  • 通知所有容器通过一个叫webnet的网络负载均衡共享80端口
  • 使用默认配置定义webnet

在运行之前,我们需要安装Compose:

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

运行新定义的的应用

$ docker swarm init
// 启动并设置应用名为getstartedlab
$ docker stack deploy -c docker-compose.yml getstartedlab
// 下线应用
$ docker stack rm getstartedlab
$ docker swarm leave --force

和服务相关的命令:

docker stack ls                                            # List stacks or apps
docker stack deploy -c <composefile> <appname>  # Run the specified Compose file
docker service ls                 # List running services associated with an app
docker service ps <service>                  # List tasks associated with an app
docker inspect <task or container>                   # Inspect task or container
docker container ls -q                                      # List container IDs
docker stack rm <appname>                             # Tear down an application
docker swarm leave --force      # Take down a single node swarm from the manager

集群(Swarm)

Swarm就是一组运行Docker并加入集群的机器。集群化之后,继续运行Docker命令将会通过swarm manager在集群上执行。swarm上机器可以是物理机也可是虚拟机,这些都是集群的一个节点。

我们可以在Compose文件中命令Swarm集群的manager使用不同的策略运行容器,例如:最空的节点,即将容器填充至最少使用的机器上、在每台机器上启动一个容器实例。

创建swarm

swarm由多个节点构成,但是总的只有两种角色:manager和worker

启用swarm模式并让当前机器成为swarm manger:

$ docker swarm init --advertise-addr <myvm1 ip>
Swarm initialized: current node (rtocnzus280j43ijh0vit7k39) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-2hlvapcqiuxghfkg6vnz3z81b3fvzu2lrn9l3ektylocnc5yvs-95cs5503f3mqnunhj7f89eboi 192.168.56.101:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

登陆其他机器,将其加入swarm集群作为worker:

docker swarm join --token <token> <ip>:<port>

其它一些命令:

// 查看集群节点
docker node ls
// 从集群中删除该机器节点,如果是manger需要加--force
docker swarm leave <--force>
//在集群上发布服务
docker stack deploy -c docker-compose.yml getstartedlab

如果还没有集群资源,可以在本机创建虚拟机进行模拟,docker支持使用Hyper-V或者VirtualBox为驱动创建虚拟机,具体可以参考:VMS ON YOUR LOCAL MACHINE (MAC, LINUX, WINDOWS 7 AND 8)

Stack

Stack就是一组可以共享依赖、相互关联的服务,一个单独的stack可以定义并协调整个应用的功能,其实在Service小节,我们创建Compose文件,并使用docker stack deploy部署服务时我们就开始接触了stack,在那一节中我们定义一个单独的服务stack,然后让它运行在一台主机上,但是在生产中我们通产不会这么做,通常我们都会有多个服务相互关联,并在多台机器上运行。

创建一个新的服务并发布

定义服务:

# docker-compose.yml
version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
    ports:
      - "80: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

发布服务:

$ docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: angi1bf5e4to03qu9f93trnxm)
Creating service getstartedlab_visualizer (id: l9mnwkeq2jiononb5ihz9u7a4)

更详细的可以参考:Get started with Docker

上传镜像至HTTP协议的私服

登陆私服

# docker login http://docker.xxxx.com 

登陆完之后,对应的授权信息会以加密的方式存储在/root/.docker/config.json中。

因为Docker默认是不支持HTTP协议的私服操作,需要编辑/etc/docker/daemon.json设置insecure-registries使其支持HTTP方式:

{
  "insecure-registries":["docker.xxxx.com"]
}

标记本地镜像

标记的母的就是将其归类到指定的仓库。

# docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][namespace/]NAME[:TAG]
例如:
# docker tag ananconda3-5.2.0:v1 docker.xxxx.com/rdc-base/ananconda3-5.2.0:v1

也可以删除标记:

# docker rmi [REGISTRYHOST/][nameSpace/]NAME[:TAG]

上传至私服

docker push docker.xxxx.com/rdc-base/ananconda3-5.2.0:v1

可能出现的问题

denied: requested access to the resource is denied

该问题一目了然是一个权限问题,通常来说问题出现在了namespace上,对应上面的就是rdc-base,说实话刚接触Docker的人会对repository name这个东西比较懵逼,和我们传统的认知有点区别,我们传统的认为仓库就是不同软件不同版本的集合,但在Docker中仓库是同一类型的镜像,只是TAG(版本)不同。

那么我们这里的rdc-base是什么呢,很多地方使用username解释的,所实话很误导,其实rdc-base就是一个仓库的命名空间(namespace),对该repository名称做了一个限定,之所以很多地方可以直接用Docker hub或者私服的账号代替,那是因为Docker hub或者私服可能默认给你创建了一个同名的namespace,而很多账号创建后不一定会有默认同名的namespace,所以可能需要网页登录并手动创建。

待我们push完我们镜像到私服上,仓库实际上的完整名称应该为:rdc-base/ananconda3-5.2.0