Docker入门

Docker入门

1. 简介

(1)容器(Container) vs 虚拟机(Virtual Machine)

容器在Linux上本机运行,并与其他容器共享主机的内核。 它运行一个独立的进程,不占用任何其他可执行文件的内存,使其轻量级。

相比之下,虚拟机(VM)运行一个完整的“客户”操作系统,通过虚拟机管理程序对主机资源进行虚拟访问。 通常,VM提供的环境比大多数应用程序需要的资源更多。

容器优势:

  1. 更高效利用系统资源
  2. 更快启动时间
  3. 一致的运行环境
  4. 轻松迁移
  5. 容易维护拓展
  6. 持续交付和部署

(2) 容器(container) 镜像(image) 仓库(repository) registry

镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数。镜像不包含任何动态数据,其内容再构建之后不会改变。

容器是镜像运行时的实体,可以被创建、启动、停止、删除、暂停等。容器的实质是独立的进程。

仓库是镜像的集合,仓库有多个Tag,每个Tag对应一个镜像。

Registry是仓库的集合,是一种集中存储、分发镜像的服务。公开的registry有docker hub等,也可以搭建私有的registry

2. Docker安装

docker store注册登录,下载dmg安装
https://store.docker.com/editions/community/docker-ce-desktop-mac

(3) 简单配置

启动docker,登录docker id

添加国内镜像地址 http://cbe850dc.m.daocloud.io

(3) 验证安装

docker --version #版本信息
docker version #详细版本信息
docker info #统计信息

docker run hello-world #从默认registry的镜像仓库下载并运行hello-world镜像
docker image ls #显示本地已有的镜像,其中有hello-world镜像

2. 实现hello-docker

该部分利用docker容器技术,实现了一个基于flask的网页

(1) 准备

ifconfig -a#查看本机在虚拟网络vboxnet1中的IP地址,192.168.99.1
#vboxnet1: inet 192.168.99.1 netmask 0xffffff00 broadcast 192.168.99.255

mkdir hello#创建空目录hello
cd hello #进入目录
touch Dockerfile #创建Dockerfile, docker镜像描述文件
touch app.py #创建app.py, flask网页服务端代码
touch requirements.txt #创建python额外依赖包列表文件

(2) Dockerfile

# 使用docker官方提供的python运行环境镜像
FROM python:2.7-slim

# 设置工作目录
WORKDIR /app

# 将当前目录下的内容拷贝到工作目录
ADD . /app

# 安装python镜像额外的python包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 把容器的80端口暴露到外部
EXPOSE 80

# 定义环境变量
ENV NAME Docker

# 容器启动运行的命令
CMD ["python", "app.py"]

(3) app.py

# -*- coding: utf-8 -*- 
from flask import Flask
from redis import Redis, RedisError
import os
import socket

# 连接Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
    
#创建Flask网页
app = Flask(__name__)

#定义访问网页根目录的处理逻辑
@app.route("/")
def hello():
    try:
        visits = redis.incr("counter")
    except RedisError:
        visits = "cannot connect to Redis, counter disabled"

    html = "

Hello {name}!

" \ "Hostname: {hostname}
" \ "Visits: {visits}" return html.format(name=os.getenv("NAME", "Docker"), hostname=socket.gethostname(), visits=visits) if __name__ == "__main__": #制定Flask网页服务端运行在80端口,接受所有ip对服务端的80端口的访问 app.run(host='0.0.0.0', port=80)

(4)requirements.txt

Flask
Redis

(5)创建hello镜像

docker build -t hello . #创建镜像,-t指定镜像名称
docker image ls #查看新创建的镜像
#REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
#hello        latest              d01960578789        41 seconds ago      132MB
#python              2.7-slim            02ca219cf841        2 weeks ago         120MB

(6)运行hello

docker run -d -p 4000:80 hello #由hello镜像创建容器,-d让容器后台运行,-p指定宿主机器与容器间的端口映射

浏览器访问 http://192.168.99.1:4000


(7)操作docker镜像和容器

docker --help #查看docker帮助
docker image --help #查看image帮助
docker container --help #查看container帮助

docker image ls #查看本地已有image列表
docker image rm 完整镜像名称/镜像id/镜像id前缀 #删除镜像

docker container ls #查看正在运行的容器列表
docker container stop 容器名称/容器id #终止运行的容器
docker container rm 容器名称/容器id #删除运行的容器

(8)分享镜像

docker login #用docker hub的账号密码登录

docker tag hello liangkw16/hello:v1 #给镜像打标签 标签格式:docker hub用户名/仓库标识:版本标识

docker image ls #查看打过标签的image, 一个image可以有多个tag

docker push liangkw16/hello:v1 #向默认registry提交镜像

docker container ls#
#CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
#18eb9e1f1b46        hello               "python app.py"     14 minutes ago      Exited (0) 10 minutes ago                       clever_chebyshev

docker container stop 18e#停止容器,18e为容器id前缀
docker container rm 18e#删除容器,18e为容器id前缀
docker image rm liangkw16/hello:v1 #删除本地镜像
docker image rm hello:latest #删除本地镜像

docker run -p 4000:80 liangkw16/hello:v1 #从registry获取镜像并运行

(9)创建私有仓库

为保证镜像上传和下载的速度,搭建一个本地测试用的私有仓库,地址为 192.168.99.1:5000, 客户端配置insecure-registries,并重启docker

docker run -d -p 5000:5000 --restart=always --name registry registry:2 #通过运行官方镜像registry来创建私有仓库,默认仓库的位置是/var/lib/registry, 可以用 -v 上传路径:下载路径 来指定镜像文件上传和下载路径
 
docker image ls#查看本地镜像
#REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
#liangkw16/hello     v1                  32f1961021f9        18 minutes ago      132MB
#registry            2                   b2b03e9146e1        9 days ago          33.3MB
#python              2.7-slim            02ca219cf841        2 weeks ago         120MB

docker tag python:2.7-slim 192.168.99.1:5000/python:2.7-slim #将本地已下载python镜像重新标签
docker push 192.168.99.1:5000/python:2.7-slim #将python镜像上传到本地registry

修改Dockerfile中的From后的python镜像为本地镜像

# 使用docker官方提供的python运行环境镜像
FROM 192.168.99.1:5000/python:2.7-slim

# 设置工作目录
WORKDIR /app

# 将当前目录下的内容拷贝到工作目录
ADD . /app

# 安装python镜像额外的python包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 把容器的80端口暴露到外部
EXPOSE 80

# 定义环境变量
ENV NAME Docker

# 容器启动运行的命令
CMD ["python", "app.py"]

重新build镜像,并push到本地私有仓库

docker build -t hello .#重新build镜像
docker tag hello:latest 192.168.99.1:5000/hello:v1 #重新给镜像打标签
docker push 192.168.99.1:5000/hello:v1 #将镜像push到本机私有仓库
docker image rm 192.168.99.1:5000/hello:v1 #删除本地镜像
docker run -p 4000:80 192.168.99.1:5000/hello:v1 #从本地私有仓库拉取镜像并运行

3. 负载均衡实现

在分布式应用程序中,应用程序的不同部分称为“服务”(services)。例如,一个视频共享站点,它包括存储应用程序数据的数据库服务,处理用户上传内容的转码服务,响应用户操作的前端服务等等。

服务只运行一个镜像,但指定了镜像的运行的方式——应该使用哪些端口,应该运行多少个容器副本,以及服务所需的容量等。 可以动态更改运行中服务的容器数量,从而为服务分配更多计算资源。

(1) 创建docker-compose.yml

version: "3" #版本号
services: #服务
  web: #服务名称
    image: 192.168.99.1:5000/hello:v1 #指定镜像
    deploy: #服务部署
      replicas: 5 #容器实例数量
      resources: #资源情况
        limits: #资源限制
          cpus: "0.1" #cpu占用不能超过10%
          memory: 50M #内存占用不能超过50M
      restart_policy: #重启策略
        condition: on-failure #失败了就重启
    ports: #外部端口和内部端口的映射
      - "4000:80"
    networks: #指定网络,容器实例共享80端口
      - webnet
networks: #负载均衡网络定义
  webnet:

(2) 运行负载均衡应用

docker swarm init #初始化swarm集群,只有本机一个节点,保证下一句不出错

docker stack deploy -c docker-compose.yml hello-service #通过docker-compose.yml创建名为hello-servcie的服务,-c指定文yml件

docker service ls #查看服务列表
#ID                  NAME                MODE                REPLICAS            IMAGE                     PORTS
#43fhqq2h0aot        hello-service_web   replicated          5/5                 192.168.99.1:5000/hello:v1   *:4000->80/tcp

docker service ps hello-service_web #查看每一个容器实例,加后缀_web
#ID                  NAME                  IMAGE                     NODE                    DESIRED STATE       CURRENT STATE            ERROR               PORTS
#q3ovtqbyc9km        hello-service_web.1   192.168.99.1:5000/hello:v1   linuxkit-025000000001   Running             Running 11 seconds ago
#jjtvp066ugg1        hello-service_web.2   192.168.99.1:5000/hello:v1   linuxkit-025000000001   Running             Running 10 seconds ago
#yomq5bxykbth        hello-service_web.3   192.168.99.1:5000/hello:v1   linuxkit-025000000001   Running             Running 11 seconds ago
#ca59l34iacyq        hello-service_web.4   192.168.99.1:5000/hello:v1   linuxkit-025000000001   Running             Running 11 seconds ago
#oy0rqrrrsgrf        hello-service_web.5   192.168.99.1:5000/hello:v1   linuxkit-025000000001   Running             Running 11 seconds ago

docker container ls -q #显示容器id,也就是网页显示的hostname

浏览器访问 http://192.168.99.1:4000, 不断刷新,发现hostname在不断变化,说明service以某种顺序安排不同的容器处理请求,以实现负载均衡

(3) 动态修改service

#修改docker-compose.yml中的replicas: 5为replicas: 6
docker stack deploy -c docker-compose.yml hello-service #重新部署一下,动态对服务的计算资源进行了修改
docker service ls #查看服务列表
#ID                  NAME                MODE                REPLICAS            IMAGE                     PORTS
#43fhqq2h0aot        hello-service_web   replicated          6/6                 192.168.99.1:5000/hello:v1   *:4000->80/tcp

(4) 关闭servcie

docker stack rm hello-service #关闭servcie
docker swarm leave --force #关闭swarm集群

4. swarm集群

Swarm是一组运行Docker并加入群集的计算机,这些机器可以是物理机或虚拟机,被称为节点。

  1. swarm manager节点:执行docker命令,管理swarm集群
  2. worker节点:提供计算资源

Swarm集群运行容器的策略有多种,比如:“emptiest node”,“global”等。

(1) 创建虚拟机

下载并安装 Virtual Box
https://download.virtualbox.org/virtualbox/5.2.14/VirtualBox-5.2.14-123301-OSX.dmg

docker-machine create --driver virtualbox myvm1#创建虚拟机
docker-machine create --driver virtualbox myvm2#创建虚拟机
docker-machine ls#查看已有虚拟机列表
#NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
#myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v18.05.0-ce
#myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v18.05.0-ce

(2) 搭建swarm集群

docker-machine ssh myvm1 "docker swarm init --advertise-addr " #myvm1作为swarm manager node,为上一步myvm1的ip
#docker swarm join --token SWMTKN-1-3gux768xxiq33fw6ln1yyfcuf5j6ke5ornvgjxyhzw0f4ptve7-7anykb6sdobckza49q4awwswu 192.168.99.100:2377

docker-machine ssh myvm2 "docker swarm join --token  :2377" #myvm1加入到swarm集群中,为myvm1的token,为myvm1的ip

docker-machine ssh myvm1 "docker node ls" #查看swarm集群中的节点
#ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
#abs2u42k4mfi92iwq6akdlqfv *   myvm1               Ready               Active              Leader              18.05.0-ce
#ne2mz38eyriey3alyva8dnxer     myvm2               Ready               Active                                  18.05.0-ce

(3) 配置虚拟机中docker的仓库地址

docker-machine ssh myvm1 #ssh连接myvm1

#myvm1执行如下指令
sudo touch /etc/docker/daemon.json
sudo chmod 777 /etc/docker/daemon.json 
sudo echo '{ "insecure-registries":    ["192.168.99.1:5000"] }' > /etc/docker/daemon.json
sudo reboot

docker-machine ssh myvm2 #ssh连接myvm1

#myvm2执行如下指令
sudo touch /etc/docker/daemon.json
sudo chmod 777 /etc/docker/daemon.json 
sudo echo '{ "insecure-registries":    ["192.168.99.1:5000"] }' > /etc/docker/daemon.json
sudo reboot

(4) docker命令绑定到manager节点

docker-machine env myvm1 #查看mvm1环境变量
eval $(docker-machine env myvm1) #让本机shell的命令发向manager节点
docker-machine ls #查看swarm集群列表,myvm1是当前激活节点
#NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
#myvm1   *        virtualbox   Running   tcp://192.168.99.100:2376           v18.05.0-ce
#myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v18.05.0-ce

(5) 在swarm集群上运行应用

docker stack deploy --with-registry-auth -c docker-compose.yml hello-service #myvm1执行docker命令,运行hello-docker-service
docker service ps hello-service_web #查看service运行情况,加_web后缀
#ID                  NAME                  IMAGE                        NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
#ot2ulsv5spmk        hello-service_web.1   192.168.99.1:5000/hello:v1   myvm2               Running             Running 3 seconds ago
#wzpd53kz410a        hello-service_web.2   192.168.99.1:5000/hello:v1   myvm1               Running             Running 3 seconds ago
#tmdhd3rz4vbn        hello-service_web.3   192.168.99.1:5000/hello:v1   myvm1               Running             Running 3 seconds ago
#t2z63zyswbsk        hello-service_web.4   192.168.99.1:5000/hello:v1   myvm2               Running             Running 3 seconds ago
#bmabpujg1x1z        hello-service_web.5   192.168.99.1:5000/hello:v1   myvm1               Running             Running 3 seconds ago

浏览器访问一下任意地址,并多次刷新,观察hostname变化
http://192.168.99.100:4000
http://192.168.99.101:4000

5.栈(Stack)

栈是一组相互关联的服务,它们共享依赖关系,并且可以协调和组合在一起。 单个堆栈能够定义和协调整个应用程序的功能,非常复杂的应用程序可能会使用多个堆栈。

(1) 向私有库中添加redis镜像

docker pull redis
docker tag redis 192.168.99.1:5000/redis
docker push 192.168.99.1:5000/redis

(2) 一个docker-compose.yml就定义了一个栈,services关键字定义的就是一组相关的服务,这里添加了redis这一服务,来提供存储功能,web网页要实现访问计数,就要依赖redis,同时web和redis使用相同的网络webnet

version: "3" #版本号
services: #服务
  web: #服务名称
    image: 192.168.99.1:5000/hello:v1 #指定镜像
    deploy: #服务部署
      replicas: 5 #容器实例数量
      resources: #资源情况
        limits: #资源限制
          cpus: "0.1" #cpu占用不能超过10%
          memory: 50M #内存占用不能超过50M
      restart_policy: #重启策略
        condition: on-failure #失败了就重启
    ports: #外部端口和内部端口的映射
      - "4000:80"
    networks: #指定网络,容器实例共享80端口
      - webnet
  redis:
    image: redis
    ports:
      - "6379:6379"
    volumes: #将主机/home/docker/data挂载到容器的/data目录
      - "/home/docker/data:/data"
    deploy:
      placement: #只在manager节点上存储数据
        constraints: [node.role == manager]
    command: redis-server --appendonly yes
    networks:
      - webnet
networks: #负载均衡网络定义
  webnet:

(3) manager节点创建data目录

docker-machine ssh myvm1 "mkdir ./data"

(4) 在swarm集群上运行Stack应用

docker stack deploy -c docker-compose.yml hello-service #myvm1执行docker命令,运行hello-service
docker service ls #查看服务列表
#ID                  NAME                  MODE                REPLICAS            IMAGE                        PORTS
#v1jdk5as6m9y        hello-service_redis   replicated          1/1                 redis:latest                 *:6379->6379/tcp
#6m9x1e3jp75x        hello-service_web     replicated          5/5                 192.168.99.1:5000/hello:v1   *:4000->80/tcp

浏览器访问一下任意地址,并多次刷新,观察hostname变化,观察访问次数的变化
http://192.168.99.100:4000
http://192.168.99.101:4000

(5) 移除应用,重启swarm集群

docker stack rm hello-service#移除服务
eval $(docker-machine env -u) #恢复本机docker环境
docker-machine ls #查看本机的虚拟机
docker-machine stop myvm1 myvm2 #关闭虚拟机
docker-machine start myvm1 myvm2 #启动虚拟机

6. 总结

(1) Docker引擎

Docker Engine是一个C/S架构的应用程序,包含以下主要组件:

  1. Docker守护进程——监听并响应docker API请求和docker管理命令
  2. REST API——提供程序与守护进程进行通信的接口
  3. 命令行接口客户端——docker命令


(2) Docker架构

Docker使用C/S架构。 Docker客户端与Docker守护进程通信,后者负责构建,运行和分发Docker容器。 Docker客户端和守护程序可以在同一系统上运行,也可以将Docker客户端连接到远程Docker守护程序。 Docker客户端和守护程序使用REST API,通过UNIX套接字或网络接口进行通信。


image.png
  1. Docker守护进程
  2. Docker客户端
  3. Docker注册仓库
  4. Docker对象
    1. 镜像(image)
    2. 容器(container)
    3. 服务(service)

(3) Docker实现技术

Docker是用Go编写的,利用了Linux内核的机制来实现了其功能,主要有以下技术:

命名空间(Namespaces)

Docker使用称为命名空间的技术来提供称为容器的隔离工作空间。 当您运行容器时,Docker会为该容器创建一组名称空间。这些命名空间提供了一层隔离。 容器的每个方面都在一个单独的命名空间中运行,其访问权限仅限于该命名空间。

控制组(Control groups)

cgroup将应用程序限制为特定的一组资源。 控制组允许Docker Engine将可用的硬件资源共享给容器,并可选择强制执行限制和约束。 例如,您可以限制特定容器的可用内存。

UnionFS(Union file systems)

UnionFS是通过创建图层来操作的文件系统,使它们非常轻量级和快速。Docker Engine使用UnionFS为容器提供构建块。 Docker Engine可以使用多种UnionFS变体,包括AUFS,btrfs,vfs和DeviceMapper。

容器格式(container format)

Docker Engine将命名空间,控制组和UnionFS组合到一个称为容器格式的包装器中。 默认的容器格式是libcontainer。 将来,Docker可以通过与BSD Jails或Solaris Zones等技术集成来支持其他容器格式。

(4) Docker进阶内容请参考官方文档

https://docs.docker.com

你可能感兴趣的:(Docker入门)