docker练习-容器和服务
使用定义容器 Dockerfile
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
ADD . /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
然后app.py
将它们放在同一个文件夹中Dockerfile
。这完成了我们的应用程序,您可以看到它非常简单。当上述Dockerfile
被内置到的图像,app.py
并且 requirements.txt
是因为存在Dockerfile
的ADD
命令,并从输出app.py
是通过HTTP得益于访问EXPOSE
命令。
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 = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {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库,应用程序打印环境变量NAME
,以及调用的输出socket.gethostname()
。最后,因为Redis没有运行(因为我们只安装了Python库,而不是Redis本身),我们应该期望在这里使用它的尝试失败并产生错误消息。
注意:在容器内部访问容器ID时,访问主机名称,这类似于正在运行的可执行文件的进程ID。
构建应用程序
$ ls
Dockerfile app.py requirements.txt
现在运行build命令。这会创建一个Docker镜像,使用-t
它来标记,因此它具有友好的名称。
docker build -t friendlyhello .
$ docker images
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398
Docker ID登录
登录本地计算机上的Docker公共注册表。
$ docker login
标记图像
将本地映像与注册表上的存储库相关联的表示法是 username/repository:tag
docker tag image wangshu19930818/
friendlyhello:v1
发布图像
将标记的图像上传到存储库:
docker push
wangshu19930818/
friendlyhello:v1
从远程存储库中拉出并运行映像
从现在开始,您可以使用docker run
以下命令在任何计算机上使用和运行您的应用程序:
docker run -p 4000:80
wangshu19930818/
friendlyhello:v1
关于服务
在分布式应用程序中,应用程序的不同部分称为“服务”。
如果想象一个视频共享站点,它可能包括一个用于将应用程序数据存储在数据库中的服务,一个用于在后台进行视频转码的服务。用户上传内容,前端服务等。
服务实际上只是“生产中的容器”。服务只运行一个映像,但它编码了映像的运行方式 – 它应该使用哪些端口,应该运行多少个容器副本,以便服务具有所需的容量,以及等等。
扩展服务会更改运行该软件的容器实例的数量,从而为流程中的服务分配更多计算资源。
使用Docker平台定义,运行和扩展服务非常容易 – 只需编写一个docker-compose.yml
文件即可。
docker-compose.yml
档案
一个docker-compose.yml
文件是一个YAML文件,它定义了如何Docker容器在生产中应表现。
docker-compose.yml
将此文件保存为docker-compose.yml
您想要的任何位置。
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: wangshu19930818/friendlyhello:v1
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:
该docker-compose.yml
文件告诉Docker执行以下操作:
-
拉上传的图像从注册表。
-
将该映像的5个实例作为一个被调用的服务运行
web
,限制每个实例使用,最多10%的CPU(跨所有内核)和50MB的RAM。 -
如果一个失败,立即重启容器。
-
将主机上的端口4000映射到
web
端口80。 -
指示
web
容器通过称为负载平衡的网络共享端口80webnet
。(在内部,容器本身web
在短暂的端口发布到 80端口。) -
webnet
使用默认设置(负载平衡的覆盖网络)定义网络。
运行新的负载均衡应用
在我们docker stack deploy
首先运行命令之前:
docker swarm init
注意:如果您没有运行,docker swarm init
则会收到“此节点不是群集管理器”的错误。
docker stack deploy -c docker-compose.yml getstartedlab
我们的单个服务堆栈在一台主机上运行已部署映像的5个容器实例。
在我们的应用程序中获取一项服务的服务ID:
docker ps
列出服务任务:
docker service ps getstartedlab_web
如果您只列出系统上的所有容器,则任务也会显示,但不会被服务过滤:
docker ps -q
curl -4 http://localhost:4000
连续多次运行,或者在浏览器中转到该URL并点击刷新几次。
扩展应用程序
可以通过更改replicas
值docker-compose.yml
,保存更改并重新运行docker stack deploy
命令来扩展应用程序:
docker stack deploy -c docker-compose.yml getstartedlab
Docker执行就地更新,无需首先拆除堆栈或杀死任何容器。
现在,重新运行docker ps -q
以查看已重新配置的已部署实例。
如果放大副本,则会启动更多任务,从而启动更多容器。
取下应用程序和群
-
将应用程序删除
docker stack rm
:docker stack rm getstartedlab
-
取下群。
docker swarm leave --force