初识Docker

Docker

初步了解Docker

本来是在学习NoSql的,突然在学习redis的时候,看到之前了解过的docker,由于本人每次想要搭建集群的时候,总是苦于没有足够的node,所有不得不申请多个虚拟机以满足自己的需求,而每个人申请的虚拟机个数有限,所有对集群的学习,总是会哑然而止,刚好碰到docker这个平台通过创建container以模拟集群。于是尝试的打开docker官网看了起来,结果一发不可收拾,阅读了下docker的概念和使用,并尝试动手用docker创建了一个hadoop2.7.6,spark2.的集群,使用之后感觉非常方便。下面我就是随着我之前学习的足迹来记录学习结果。

用docker官网的话来说docker,Docker就是一个通过container用来开发,部署,运行application的一个平台。而使用container来部署application的过程叫做Containerization集装箱化

通过Containerization来部署一个application是一件非常简单的事情。因为这个过程是灵活,轻量,可重用,具有伸缩性,stackable(这个词的意思就是同时运行多个不相干或者相关的服务都可以,就翻译成具有堆叠性)

那到底什么是docker呢,什么是docker平台,还是用docker官方解释来介绍吧! 什么是docker,用一张图来感受一下什么是docker平台!

既然要学习docker,那首先有几个概念需要了解:

Comtainers:通过运行一个image镜像启动一个container,这个container就是一个image的运行实例

images:一个images是一个可以执行的包,其中包含运行一个application的所有东西,例如代码,库,环境变量和配置文件等等

services:一个application的不同组件,比如要设计一个视频共享网站,就需要一个把application数据存储进数据库的数据库service,一个在用户上传视频之后,后台用于视频编码转换的编码service,一个用于前端的restful API service.

Swarms: swarm是一个docker集群,在每个node上运行docker daemon.其中有master,worker

stacks: stack是由不相干的services组成,这些services能够共享依赖,并且能够互相协调和共同缩放的。单个stack 能够定义和协调整个application功能。

估计看的似懂非懂,没关系,每个概念我们之后都会涉及。

Docker与虚拟机

VM: 通过一个vm hypervisor去访问主机资源,然后通过vm hypervisor 去运行一个完整的Guest OS,这个Guest OS虚拟机提供了一个比大多数application需要的资源还要大的运行环境。

Docker Container: container 运行在linux机器上,并且多个container共享宿主机的内核。他们跑在各个互不关联的进程中,相比于其他的可执行的进程来说,不会占用更多的内存,因此更加的轻量。所以docker container 的优势体现在秒级启动和占用资源少

安装

Mac电脑安装docker非常简单。下载Docker.dmg,直接安装即可。docker支持的平台有Mac,Window,linux系统(CensOS,Debian,Fefora,Ubuntu),云平台等等。其他平台的docker安装流程参照这里

简单体验一下Docker命令

安装好docker之后,自己可以在CLI里运行下下面的命令,了解下docker的基础命令。

## 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 imagels

## List Docker containers (running, all, all in quiet mode)

docker containerls

docker containerls--all

docker containerls-aq

在这里提一下docker run hello-world,运行这命令的docker的操作步骤如下:

Docker took the following steps:

The Docker client contacted the Docker daemon.

The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)

The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.

The Docker daemon streamed that output to the Docker client, which sent itto your terminal.

images 和 Container

在上一节中,docker daemon可以从Docker Hub中可以抓取到"hello-world" image。那我们怎么创建自己的image呢?利用官方的例子来走一遍

Dockerfile

我们可以通过一个叫Dockerfile的文件定义一个image。在这个文件里,我们可以定义我们所需要的application运行环境。现在让我们来创建我们自己的image吧。

该image的运行环境:在container中创建一个debian系统,在一个debian的linux系统中安装了Python2.7,设置/app为工作目录,接着把宿主机的当前目录下的所有文件都拷贝到container中的/app目录下,并且通过pip安装工具安装了flask和redis,并且把端口80暴露给宿主机,且设置了环境变量NAME为World,最后在container启动的时候通过运行app.py

创建一个新的空目录,然后在这个目录创建一个Dockerfile文件,并且把下列复制粘贴到Dockerfile中,该Dockerfile所描述的运行:

Dockerfile

# Use an official Python runtime as a parent image

FROMpython:2.7-slim

# Set the working directory to /app

WORKDIR/app

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

ADD. /app

# Install any needed packages specified in requirements.txt

RUNpip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container

EXPOSE80

# Define environment variable

ENVNAME World

# Run app.py when the container launches

CMD["python", "app.py"]

其中requirements.txt和app.py还没创建,别急。

在当前目录中,创建requirements.txt和app.py。

requirements.txt

Flask

Redis

app.py

fromflaskimportFlask

fromredisimportRedis,RedisError

importos

importsocket

# Connect to Redis

redis=Redis(host="redis",db=,socket_connect_timeout=2,socket_timeout=2)

app=Flask(__name__)

@app.route("/")

defhello():

try:

visits=redis.incr("counter")

exceptRedisError:

visits="cannot connect to Redis, counter disabled"

html="Hello !"\

"Hostname:

"\

"Visits: "

returnhtml.format(name=os.getenv("NAME","not found"),hostname=socket.gethostname(),visits=visits)

if__name__=="__main__":

app.run(host='0.0.0.0',port=80)

开始构建app了,cd到新的空目录,里面已经有Dockerfile,app.py,requirements.txt三个文件了

# create a Docker image, -t : specify Repository Name and optionally a tag in the 'name:tag' format

$ dockerbuild-tfriendlyhello:test .

$ dockerimagels

REPOSITORY TAG IMAGE ID CREATED SIZE

friendlyhello test fd222ed0835e4days ago 151MB

运行 app

# -p : 需要mapping 宿主机的port 4000 到 container的暴露的port 80

# -d : 后台运行container,或者说是application,可以通过 docker constainer stop CONTAINER_ID 停止constaier

$ dockerrun-p4000:80 friendlyhello:test

* Serving Flask app"app"(lazy loading)

* Environment: production

WARNING: Do not use the development serverina production environment.

Use a production WSGI server instead.

* Debug mode: off

* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

上面的信息是来至于container内部,所以http://0.0.0.0:80是指container内部的port80已经启动,通过映射到宿主机port4000,我们就可以访问http://localhost:4000 正确的URL

共享你的images

在简单体验一下Docker命令这一节中,我们可以直接在任意的docker daemon中运行docker run hello-world,这是因为hello-world的image是存储在Docker hub中,这样就可以远程拉取下来运行。当然我们也可以制作自己的images上传到docker hub中去。docker hub类似一个github的一个docker中央image库。

创建自己的Docker账户

然后就可以在CLI中登录Docker账户

$ dockerlogin

给你的image加标签

通过docker tag image命令先给image加标签,tagname是可选的,不写的话,默认的就是latest。

$ dockertag image:tag username/repository:tag

For example:

$ dockertag friendlyhello:test cnicetoupp/mydocker:test

$ dockerimagels

REPOSITORY TAG IMAGE ID CREATED SIZE

cnicetoupp/mydocker test fd222ed0835e4days ago 151MB

上传image

上传你的image到docker hub中的repo中

$ dockerpush username/repository:tag

例如:

$ dockerpush cnicetoupp/mydocker:test

一旦完成上传,你就可以登录到Docker_hub中去看下你的新image。

运行在远程仓库中的image

$ dockerrun-plocalport:containerport username/repository:tag

例如:

$ dockerrun-p4000:80 cnicetoupp/mydocker:test

Services 和 stack,swarm

一个service本质上就是仅仅运行一个image,然而service则定义了一些image的运行配置,比如它应该使用哪些port,按照service的需求来应该启动多少个container的副本等等。我们可以通过docker-compos.yml来定义services,运行services,并且规模化services等等。

一个stack是由一组相互关联的services构建而成,这些services共享依赖,并且能够进行编排和具有伸缩性。

一个application可以由单个stack构成,如果这个application是个非常复杂的,那么这个application也可以由多个stack构成。

官方解释swarm是运行docker的一组机器,其实简而言之就是docker集群。

docker-compose.yml

创建docker-compose.yml

version:"3"

services:

web:

# replace username/repo:tag with your name and image details

image:cnicetoupp/mydocker:test(username/repo:tag)

deploy:

replicas:5

restart_policy:

condition:on-failure

resources:

limits:

cpus:"0.1"

memory:50M

ports:

-"81:80"

networks:

-webnet

visualizer:

image:dockersamples/visualizer:stable

ports:

-"8081:8080"

volumes:

-"/var/run/docker.sock:/var/run/docker.sock"

deploy:

placement:

constraints:[node.role==manager]

redis:

image:redis

ports:

-"6379:6379"

volumes:

-"/home/docker/data:/data"

deploy:

placement:

constraints:[node.role==manager]

command:redis-server--appendonlyyes

networks:

-webnet

networks:

webnet:

上述的docker-compose.yml 文件告诉Docker 创建了两个server,一个叫web,一个叫visualizer

web service 做的事情包括

从docker hub中把cnicetoupp/mydocker:testimage拉去下来。

运行这个web service,也就是运行cnicetoupp/mydocker:testimage的5个container实例。

一旦其中一个container运行失败则立刻重启container。

Map 宿主机的81端口到web server的80端口。

通过称为webnet的负载均衡网络来告知各个web containers的80端口。

以默认的设置定义了webnet网路,这个网络是一个负载均衡的网络。

visualizer service 做的事情包括

从docker hub中把dockersamples/visualizer:stableimage拉取下来。

Map 宿主机的8081端口到web server的8080端口。

volumes这个key代表的是把宿主机的/var/run/docker.sock文件映射到visualizer service的/var/run/docker.sock 文件

限制visualizer service只能运行在swarm中的manager node上(swarm后续会讲)

Redis service 做的事情包括:启动redis 服务

创建swarm集群

考虑到需要用到虚拟机,自己先在电脑上install Oracle VirtualBox

创建两个VMs

$ docker-machinecreate--drivervirtualbox myvm1

$ docker-machinecreate--drivervirtualbox myvm2

$ docker-machinels

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

初始化swarm集群和添加node

设置myvm1为manager,可以执行集群管理的命令和协调node之间的协作,也可以用来运行container

设置myvm2为worker,用来运行container。

初始化myvm1 manager和加入myvm2

$ docker-machinesshmyvm1"docker swarm init --advertise-addr

Swarm initialized: currentnode is now a manager.

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

docker swarm join \

--token \

:

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

$ docker-machinesshmyvm2"docker swarm join \

--token \

:2377"

Thisnodejoined a swarm as a worker.

例如:

$ docker-machinesshmyvm1"docker swarm init --advertise-addr 192.168.99.100"

Swarm initialized: currentnode(v5fnq6cz4hhm5b54k09rfo64y) is now a manager.

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

docker swarm join--tokenSWMTKN-1-2sj9asp6d7dqtsjddationslh1uivujuzu3sxmlonsgmqqpsby-cyb47npzruij6skjuhpvj0fz9192.168.99.100:2377

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

$ docker-machinesshmyvm2"docker swarm join --token SWMTKN-1-2sj9asp6d7dqtsjddationslh1uivujuzu3sxmlonsgmqqpsby-cyb47npzruij6skjuhpvj0fz9 192.168.99.100:2377"

Thisnodejoined a swarm as a worker.

$ docker-machinesshmyvm1"docker node ls"

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION

v5fnq6cz4hhm5b54k09rfo64y * myvm1 Ready Active Leader18.05.0-ce

64ac86yagklk8mmz85mivfdvi myvm2 Ready Active18.05.0-ce

到目前为止,就已经创建了一个swarm。

docker-machine-shell

我们之前是通过docker-machine与swarm manager交互,但是有另一种方法,就是通过配置一个docker-machine shell就可以在宿主机上直接运行applicaion了。

$ docker-machineenv myvm1

exportDOCKER_TLS_VERIFY="1"

exportDOCKER_HOST="tcp://192.168.99.100:2376"

exportDOCKER_CERT_PATH="/Users/che/.docker/machine/machines/myvm1"

exportDOCKER_MACHINE_NAME="myvm1"

# Run this command to configure your shell:

# eval $(docker-machine env myvm1)

$ eval$(docker-machine env myvm1)

$ dockernodels

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION

v5fnq6cz4hhm5b54k09rfo64y * myvm1 Ready Active Leader18.05.0-ce

64ac86yagklk8mmz85mivfdvi myvm2 Ready Active18.05.0-ce

通过这种方式和docker-machine ssh myvm1 "docker node ls"效果一样,此外还可以使用本地的docker-compose.yml文件,不用远程拷贝到myvm1,就可以直接部署application了

关闭docker-machine shell

$ eval$(docker-machine env -u)

运行stack application确保所需文件已经创建好

需要的文件Dockerfile,app.py和requirements docker-compose.yml

$ ls

Dockerfileapp.pydocker-compose.ymlrequirements.txt

在swarm manager机器上创建./data目录

$ docker-machinesshmyvm1"mkdir ./data"

配置一个到myvm1的docker-machine shell

可参考上一节的配置一个到swarm_manager的一个docker-machine-shell

运行application

$ dockerstack deploy-cdocker-compose.yml getstartedlab

查看你启动的sevice

$ dockerservicels

ID NAME MODE REPLICAS IMAGE PORTS

ul8y7savp5kd getstartedlab_redis replicated1/1 redis:latest *:6379->6379/tcp

ltlzvf79ptb8 getstartedlab_visualizer replicated1/1 dockersamples/visualizer:stable *:8080->8080/tcp

p61ww9z94h3l getstartedlab_web replicated5/5 cnicetoupp/mydocker:test *:80->80/tcp

查看web service页面

可以通过访问 http://192.168.99.100:81或者 http://192.168.99.101:81,都可以看到界面,因为,我们web service在有5个副本,这个5个副本随机运行在swarm集群中。

下图中没有加上81端口,是因为在docker-compose.yml中,把web service的port参数时,设置成同一个port且是第一个service就可以不用加端口直接访问。

查看visualizer service界面

可以通过访问 http://192.168.99.100:8081/ ,如果你是按照我的docker-compose.yml的内容配置的话,就用8081端口,我是故意把两个map端口区分开来,以便区别宿主机和container端口的区别,如果你用的官网的docker-compose.yml.就用8080端口即可,下图即为访问结果。

docker stack 其他命令查看起了哪些service

docker service ls

查看那个出错的service的log

docker service ps --no-trunc getstartedlab_redis

总结

通过上述的学习和实践之后,我更加喜欢docker,因为它给我了我更多想象力,在一台机器上,我可以模拟出任何运行环境,可以运行任何application,还可以通过swarm 和 kubernetes(k8s) 创建一个docker集群。并且可以通过docker-compose.yml编排application,作为一个喜欢探索各个领域的同学来说,简直是神器。目前更多的产品是docker+k8s的结合使用,有时间可以再学习下k8s!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180525G1ZWF300?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券