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!
领取专属 10元无门槛券
私享最新 技术干货