$ docker --version
Docker version 17.12.0-ce, build c97c6d6
可以使用docker info
或者docker version
,不要添加--
,就可以看到Docker安装的详细信息了。
$ docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 17.12.0-ce
Storage Driver: overlay2
...
$ docker image ls
列出hello-world容器的详细信息,如果容器依然在运行的话,不用使用--all
参数。
$ docker container ls --all
CONTAINER ID IMAGE COMMAND CREATED STATUS
54f4984ed6a8 hello-world "/hello" 20 seconds ago Exited (0) 19 seconds ago
## 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
在这一小节中,将介绍容器的创建。后续小节中会介绍服务(services)和栈(Stack)。
以前你写一个Python程序,要在机器上安装Python运行环境才行。
现在你只需要在Docker的镜像中配置你的应用程序需要的运行环境就行。
镜像配置在一个叫Dockerfile的文件中。
Dockerfile
文件定义一个容器。Dockerfile
文件定义了你的容器环境中会运行什么。在这个环境中的资源都会被虚拟化。
创建一个空目录,然后cd进入这个目录。创建一个叫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文件中包含两个还没创建的文件,分别是app.py
和requirements.txt
。
接下来让我们创建它们。
创建requirements.txt
,并把下面的内容拷贝进去。
Flask
Redis
创建app.py
,并把下面的内容拷贝进去。
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
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", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
pip install -r requirements.txt
命令会为python安装Flask和Redis库的。
上面这个app会获取环境变量NAME
,然后输出一些欢迎信息,包括socket.gethostname()
。由于没有安装Redis,所以只会打印一些提示信息。
注意:在容器里面访问主机名(the name of the host),实际上会检索到容器的ID值。
要开始构建app了,首先要确保你仍然在新建目录的顶层。用一下ls
命令,会展示如下的信息:
$ ls
Dockerfile app.py requirements.txt
现在开始构建。可以用--tag
参数为新建的镜像指定标签,也可以使用缩写的-t
参数。
docker build --tag=friendlyhello . #注意后面的那个小点,指定了构建的路径
构建的镜像存储在哪儿了?它被存储在本地Docker镜像仓库。
$ docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398
注意tag
默认是`latest。tag选项的完整语法是像下面这样:
--tag=friendlyhello:v0.0.1
设置代理
如果你使用了代理,那么需要在Dockerfile文件的RUN pip
命令之前加上两个环境设置:
# Set proxy server, replace host:port with values for your servers
ENV http_proxy host:port
ENV https_proxy host:port
DNS设置
DNS配置丢失会导致pip
命令执行失败。你需要把自己的dns服务器地址配置到docker配置文件中。
Docker配置文件是``/etc/docker/daemon.json(没有的话,就新建一个),将下面的dns信息添加进去:
{
"dns": ["your_dns_address", "8.8.8.8"]
}
保存daemon.json
文件,然后重启Docker服务:
sudo service docker restart
在运行app时需要将容器里面的80端口映射到主机的4000端口,可以使用-p
参数完成:
$ docker run -p 4000:80 friendlyhello
本来你应该可以在http://0.0.0.0:80
看到信息。但是容器里面的消息不知道你已经把容器的80端口映射到主机的4000端口了,所以正确的访问地址是:http://localhost:4000
。
如果你是在虚拟机中运行的容器,那么要把localhost换成虚拟机的IP地址,比如:http://192.168.10.134:80
也可以使用curl命令在终端看到同样的信息:
$ curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
在终端使用ctrl+c
可以停止app应用的运行。
注意:在windows下,ctrl+c
执行后,还需要使用docker container stop
命令去停止容器的执行。
现在我们在背景中运行上述app:
docker run -d -p 4000:80 friendlyhello
上面的命令会返回容器的完整ID值。
你可以使用docker container ls
查看容器的ID。
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
要注意容器ID值在http://localhost:4000中匹配的是什么。(匹配的就是getLocalhost())
现在可以使用docker container stop
停止应用,后面跟上容器的ID值,例如:
$ docker container stop 1fa4ab2cf395
用你的账号登录:
首先你要在 hub.docker.com上注册一个账号。
$ docker login
标记你的镜像:
标记镜像的语法如下:
docker tag image username/repository:tag
例如:
docker tag friendlyhello gordon/get-started:part2
可以查看你标记的镜像:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest d9e555c53008 3 minutes ago 195MB
gordon/get-started part2 d9e555c53008 3 minutes ago 195MB
python 2.7-slim 1c7128a655f6 5 days ago 183MB
...
发布镜像:
docker push username/repository:tag
这条命令会将你的镜像上传到 Docker Hub,如果你登录该网站,可以看到相应的镜像。
从远程仓库拉取镜像并运行
现在你可以在任何机器上运行你的APP,语法为:
docker run -p 4000:80 username/repository:tag
如果本地仓中没有对应镜像,Docker就会从远程仓库中拉取:
$ docker run -p 4000:80 gordon/get-started:part2
Unable to find image 'gordon/get-started:part2' locally
part2: Pulling from gordon/get-started
10a267c67f42: Already exists
f68a39a6a5e4: Already exists
9beaffc0cf19: Already exists
3c1fe835fb6b: Already exists
4c9f1fa8fcb8: Already exists
ee7d8f576a14: Already exists
fbccdcced46e: Already exists
Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068
Status: Downloaded newer image for gordon/get-started:part2
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
主要用到下面的一些基本命令:
docker build -t friendlyhello . # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello # Run "friendlyhello" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello # Same thing, but in detached mode
docker container ls # List all running containers
docker container ls -a # List all containers, even those not running
docker container stop <hash> # Gracefully stop the specified container
docker container kill <hash> # Force shutdown of the specified container
docker container rm <hash> # Remove specified container from this machine
docker container rm $(docker container ls -a -q) # Remove all containers
docker image ls -a # List all images on this machine
docker image rm <image id> # Remove specified image from this machine
docker image rm $(docker image ls -a -q) # Remove all images from this machine
docker login # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag # Tag for upload to registry
docker push username/repository:tag # Upload tagged image to registry
docker run username/repository:tag # Run image from a registry
在分布式应用中,应用的不同模块叫做“Services”。例如,电影共享网站,有数据存储服务,视频转码服务,页面展示服务等。
当应用的服务需要扩展的时候,Docker可以很好的处理,仅仅需要写一个docker-compose.yml
文件。
docker-compose.yml
文件一个docker-compose.yml
是一个YAML文件,其定义了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做下面的事情:
web
, limiting each one to use, at most, 10% of a single core of CPU time (this could also be e.g. “1.5” to mean 1 and half core for each), and 50MB of RAM.web
’s port 80.web
’s containers to share port 80 via a load-balanced network called webnet
. (Internally, the containers themselves publish to web
’s port 80 at an ephemeral port.)webnet
network with the default settings (which is a load-balanced overlay network).在执行docker stack deploy
前,先运行下面的命令:
docker swarm init
注意: swarm的概念后面会讲,这儿不运行这个命令的话,后面会报错。
现在开始运行应用,我们给它一个名字getstartedlab:
docker stack deploy -c docker-compose.yml getstartedlab
这个单一服务栈会运行镜像的5个实例在一台机器上。
查看这个应用中唯一服务的ID值:
docker service ls
下面的命令会展示所有关联到getstartedlab
栈的所有服务:
docker stack services getstartedlab
ID NAME MODE REPLICAS IMAGE PORTS
bqpve1djnk0x getstartedlab_web replicated 5/5 username/repo:tag *:4000->80/tcp
在一个服务中的单个容器叫做任务(task)。每个task会有独一无二的ID值。
列出服务中的任务:
docker service ps getstartedlab_web
你可以运行curl -4 http://localhost:4000
或者在浏览器中访问http://localhost:4000
多次。
每一次访问都可以发现展示容器ID值发生了改变。
我们可以使用docker stack ps
查看一个应用的所有任务,例如:
docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
uwiaw67sc0eh getstartedlab_web.1 username/repo:tag docker-desktop Running Running 9 minutes ago
sk50xbhmcae7 getstartedlab_web.2 username/repo:tag docker-desktop Running Running 9 minutes ago
c4uuw5i6h02j getstartedlab_web.3 username/repo:tag docker-desktop Running Running 9 minutes ago
0dyb70ixu25s getstartedlab_web.4 username/repo:tag docker-desktop Running Running 9 minutes ago
aocrb88ap8b0 getstartedlab_web.5 username/repo:tag docker-desktop Running Running 9 minutes ago
可以通过改变docker-compose.yml
文件中的replocas
值来更改应用的规模。改动文件后保存,然后运行重新运行docker stack deploy
命令:
docker stack deploy -c docker-compose.yml getstartedlab
Docker会在内部更新,不需要停掉或者杀掉任何的容器。
现在可以用docker container ls -q
命令查看新部署的容器。
使用 docker stack rm
停掉应用:
docker stack rm getstartedlab
停掉集群(swarm).
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
可以将机器加入到Docker化的集群中,实现多机器、多容器应用,这个集群就叫作Swarm
一个集群就是一组运行Docker的机器,并且他们都加入到了集群。
集群中有个manager节点,其余节点都是worker。可以在manager节点执行Docker命令,worker只是提供一些能力。
执行的Docker命令还是之前的一样,不过之前是在一台主机上执行,现在是在一个集群上执行。
可以在构建文件(the compose file)文件中指定manager分配容器的策略。
之前是在单机模式 (a single-host mode)运行Docker,只需要切换到集群模式(swarm mode),就可以开启一个Swarm集群。
一个集群由多个节点组成。
设置步骤非常简单:
(1) 运行docker swarm init
让当前机器成为集群管理者(manager)。
(2) 在其他机器上运行docker swarm join
,让它们加入到集群成工作节点(workers)。
根据自己的实际情况创建两个虚拟机。我是在Vmware中安装了两个ubuntu16.04作为集群节点的。
下面是两个节点的信息:
192.168.184.134 vm-happy
192.168.184.131 vm-01
首先确保两个虚拟机中都安装了docker,然后用docker run hello-world
测试一下是否安装成功。
在vm-happy上执行下面的命令来启动Swarm集群并指定manager节点。
$ sudo docker swarm init --advertise-addr <myvm1 ip>
To add a worker to this swarm, run the following command:
docker swarm join \
--token <token> \
<myvm ip>:<port>
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
例如:
sudo docker swarm init --advertise-addr 192.168.184.134:2377
千万不能指定2376作为端口,那个端口是Docker守护进程使用的,你指定那个端口作为集群通信端口会导致一些错误出现。
记住上述命令中输出的加入到swarm集群的命令,就是有--token
的那条命令。
然后切换登录到vm-01虚拟机的终端上,执行下面的进入Swarm集群的命令:
$ docker swarm join \
--token <token> \
<ip>:2377"
This node joined a swarm as a worker.
注意要把
现在就创建好了一个swarm集群了。
可以用docker node ls
命令查看当前集群中的节点:
mucao@vm-happy:~$ sudo docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
foj36uh7xbnzlitcj4ndxegza vm-01 Ready Active 19.03.1
6nhnlmihrwewek10shdje33xy * vm-happy Ready Active Leader 19.03.1
如果要将一个worker节点脱离swarm集群,可以在该worker节点执行下面的命令:
docker swarm leave
在Swarm部署应用其实和前面在单个节点上部署一样的,前面部署应用其实可看作是在只有一个节点的集群上部署。
要在swarm集群上部署应用,就和之前在单节点部署相似就像下面的命令:
# 下面这个命令是单节点上部署应用时使用的
$ sudo docker stack deploy -c docker-compose.yml getstartedlab # 要确保docker-compose.yml在当前目录下
在集群上部署是差不多的。
(1) 登录镜像仓库。如果是私有镜像仓库,需要指定地址。
sudo docker login registry.example.com
(2) 部署应用到集群
sudo docker stack deploy --with-registry-auth -c docker-compose.yml getstartedlab
--with-registry-auth
可以把manager节点的登录信息附带发送给worker 节点,这样worker节点也就可以从镜像仓库中拉取镜像了。
接下来的操作就和之前一样了。
查看应用的task:
$ docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE
jq2g3qp8nzwx getstartedlab_web.1 gordon/get-started:part2 vm-01 Running
88wgshobzoxl getstartedlab_web.2 gordon/get-started:part2 vm-01 Running
vbb1qbkb0o2z getstartedlab_web.3 gordon/get-started:part2 vm-happy Running
ghii74p9budx getstartedlab_web.4 gordon/get-started:part2 vm-happy Running
0prmarhavs87 getstartedlab_web.5 gordon/get-started:part2 vm-01 Running
接下来可以向之前一样用(实体机上的)浏览器访问http://
curl http://
查看输出。
你可以看到5个容器的ID值会在输出内容中随机出现。
swarm集群是针对于容器做的负载均衡,无论这些容器运行在哪个机器上。
假设有一个叫my-web
的服务发布在8080端口,那么负载均衡的图示有点像下面这样:
想要扩展应用的容器数,只需要修改docker-compose.yml
文件,然后再使用docker stack deploy
命令更新应用部署就行。
扩展节点也是一样的,首先在新的节点执行docker swarm join
命令,就像在vm-01
上执行加入集群的操作一样。然后运行docker stack deploy`命令更新应用部署,就可以利用新的资源了。
可以使用docker stack rm
命令清除应用的部署,例如:
docker stack rm getstartedlab
重启的话,直接对相应的虚拟机进行手动重启就行了。
其实搭建Swarm集群很简单,先初始化manager节点,然后其他节点按照提示加入就行。其余部署应用和查看操作和在单节点上的操作是一致的,就是要注意只能在manager所在节点执行命令,worker节点只是提供了能力。
下面是用到的命令的总结:
docker node ls # List the nodes in your swarm
docker node inspect <node ID> # Inspect a node
docker swarm join-token -q worker # View join token
docker node ls # View nodes in swarm (while logged on to manager)
docker swarm leave # Make the worker leave the swarm
docker swarm leave -f # Make master leave, kill swarm
docker stack deploy -c <file> <app> # Deploy an app; command shell must be set to talk to manager (myvm1), uses local Compose file
其实栈(Stack)就相当于是一个应用,由多个服务(Service)组成。
一个栈可以是一组相互协调的服务一起运行。一个栈可以定义和协调一个应用的所有功能。
打开docker-compose.yml文件,并用下面的内容取代原始内容,注意要将username/repo:tag
替换成你镜像的详细信息。
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:
在这个文件中新增了一个服务visualzer,访问指定的端口可以可视化Docker集群中运行的Service。注意有两个新的内容,volumes关键字定义了一个数据卷,就是主机目录到容器目录的一个映射,可以让Docker访问到主机上的套接字。placement关键字确保这个服务只会运行在Swarm集群的manager节点上。
重新部署应用
$ docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: angi1bf5e4to03qu9f93trnxm)
Creating service getstartedlab_visualizer (id: l9mnwkeq2jiononb5ihz9u7a4)
访问visualizer服务。在浏览器其中访问http://localhost:8080就可以看到可视化的页面,如果swarm运行在虚拟机中,那么就将localhost替换成对应虚拟机的IP地址,比如,http://192.168.184.134:8080。
查看该stack的所有任务。
docker stack ps getstartedlab
这个visualizer是一个独立服务,不依赖别的服务。下面我们将创建一个有依赖的服务。
在docker-compose.yml添加redis服务,并保存。注意username/repo:tag要换成你自己镜像的详细信息。
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
redis:
image: redis
ports:
- "6379:6379"
volumes:
- "/home/docker/data:/data"
deploy:
placement:
constraints: [node.role == manager]
command: redis-server --appendonly yes
networks:
- webnet
networks:
webnet:
还有注意一点,"/home/docker/data:/data"中的/home/docker/data要换成你自己manager节点所在的某个文件夹路径,最好是你自己用户的家目录下的一个目录。
上面定义的redis服务,也可以通过redis桌面管理工具登录和查看,就相当于是运行了一个redis真实实例一样。
其中定义的数据卷"/home/docker/data:/data"可以让容器中的redis把数据存储到主机的目录中,即使容器被擦除了,数据依然会存在。
运行docker stack deploy一次或多次来部署应用。
$ docker stack deploy -c docker-compose.yml getstartedlab
运行docker service ls
确保这三个服务正如期望的一样在运行。
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
x7uij6xb4foj getstartedlab_redis replicated 1/1 redis:latest *:6379->6379/tcp
n5rvhm52ykq7 getstartedlab_visualizer replicated 1/1 dockersamples/visualizer:stable *:8080->8080/tcp
mifd433bti1d getstartedlab_web replicated 5/5
查看web浏览器中的显示内容。
在浏览器其中访问http://localhost:80就可以看到可视化的页面,如果swarm运行在虚拟机中,那么就将localhost替换成对应虚拟机的IP地址,比如,http://192.168.184.134:80。
也可以使用8080端口访问visualizer的服务,注意到redis服务和web、visualizer服务一起运行了。
上面的步骤演示怎么部署有多个服务的应用(stack)。
上面所有内容涉及的方面为:
安装Docker引擎(社区版)
创建Swarm集群
运行docker swarm init
去创建一个Swarm集群。
部署应用
docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
Creating service getstartedlab_visualizer
Creating service getstartedlab_redis
运行一些Swarm命令去验证应用部署。
查看集群节点
[getstartedlab] ~ $ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
n2bsny0r2b8fey6013kwnom3m * ip-172-31-20-217.us-west-1.compute.internal Ready Active Leader
查看有哪些服务
[getstartedlab] ~/sandbox/getstart $ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
ioipby1vcxzm getstartedlab_redis replicated 0/1 redis:latest *:6379->6379/tcp
u5cxv7ppv5o0 getstartedlab_visualizer replicated 0/1 dockersamples/visualizer:stable *:8080->8080/tcp
vy7n2piyqrtr getstartedlab_web replicated 5/5 sam/getstarted:part6 *:80->80/tcp
查看某个服务中的任务
[getstartedlab] ~/sandbox/getstart $ docker service ps vy7n2piyqrtr
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
qrcd4a9lvjel getstartedlab_web.1 sam/getstarted:part6 ip-172-31-20-217.us-west-1.compute.internal Running Running 20 seconds ago
sknya8t4m51u getstartedlab_web.2 sam/getstarted:part6 ip-172-31-20-217.us-west-1.compute.internal Running Running 17 seconds ago
ia730lfnrslg getstartedlab_web.3 sam/getstarted:part6 ip-172-31-20-217.us-west-1.compute.internal Running Running 21 seconds ago
1edaa97h9u4k getstartedlab_web.4 sam/getstarted:part6 ip-172-31-20-217.us-west-1.compute.internal Running Running 21 seconds ago
uh64ez6ahuew getstartedlab_web.5 sam/getstarted:part6 ip-172-31-20-217.us-west-1.compute.internal Running Running 22 seconds ago
更新应用,只需要修改docker-compose.yml文件,然后再运行docker stack deploy
命令重新部署。
使用docker stack rm
移除应用,例如:
$ docker stack rm getstartedlab
sudo service docker restart
或者使用
sudo systemctl restart docker
参考资料: 官方文档