Docker 镜像、容器与服务

在上一篇文章《Docker 集群网络规划与 VM 网络配置》中,我们准备好了三台虚拟机,虚拟机使用的是 VirtualBox ,虚拟机安装的是 ubuntu service 16.04 并安装好 docker 。如果还没有配置好的同学可以查看上一篇文章。

Docker 镜像

An image is a read-only template with instructions for creating a Docker Container.

镜像是创建 docker 容器指令的只读模板。包含一个只读的文件系统镜像,应用程序在这个文件系统上运行,并拥有属于自己的网络、内存、CPU 等资源的定义。通常,一个镜像申明了自己主进程的应用。

一个 docker 镜像可以构建于另一个 docker 镜像之上,这种层叠关系可以是多层的。第1层的镜像层我们称之为基础镜像(Base Image),其他层的镜像(除了最顶层)我们称之为父层镜像(Parent Image)。这些镜像继承了他们的父层镜像的所有属性和设置,并在Dockerfile中添加了自己的配置。如在 ubuntu 上添加自己的 apache 文件,然后在 apache 上建立 web 应用文件。

docker 镜像通过镜像 ID 进行识别。镜像 ID 是一个64字符的十六进制的字符串。但是当我们运行镜像时,通常我们不会使用镜像 ID 来引用镜像,而是使用镜像名来引用。要列出本地所有有效的镜像,可以使用命令:

1
docker images

Docker 容器

A container is a runnable instance of an image.

它会在所有的镜像层之上增加一个可写层,对现有文件变更将写在一个临时的卷上(新的一层),该主进程、相关进程、文件系统成为一个运行的实例。

每个容器创建后,有运行、暂停、中止状态,直到移除。
程序运行的产生的数据,都在容器中的文件中,Restart 不会丢失数据。
当主进程结束时,该实例同时结束运行。使用 –rm 表示运行后自动删除容器。

docker 容器可以使用命令创建:

1
docker run <imageName>

镜像与容器的区别

Docker 服务

Services allow you to scale containers across multiple Docker daemons, which all work together as a swarm with multiple nodes.

服务是一组运行于集群不同结点中可复制的容器的集合。通过服务发现机制,使用该服务的客户端可以透明的访问这些容器。这些容器的分布、升级、故障管理有集群统一管理。一般地,集群内部服务容器地选择由 动态DNS 实现。

建立私有镜像仓库

进入阿里云开发者平台 dev.aliyun.com ,注册一个帐号,然后登录。

管理中心 -> Docker镜像仓库 -> 镜像列表 -> 华南1 -> 创建镜像仓库

仓库类型选择:公开,代码源选择本地仓库,通过命令推送镜像到镜像库,当然也可以直接使用GitHub上的项目创建镜像库。

仓库使用操作: 如上传 hello-python 镜像

列表: docker images
登录: docker login –username=[your account] registry.cn-shenzhen.aliyuncs.com
tag: docker tag [ImageId] registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]
上传: docker push registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]
下载: docker push registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]
tag: docker tag registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号] hello-python
删除: docker rmi registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]
运行: docker run –rm hello-python
退出: docker logout registry.cn-shenzhen.aliyuncs.com

创建镜像

创建一个项目目录,如 pythonweb,然后 cd 到该目录

1
mkdir pythonweb && cd pythonweb

创建一个简单 web 应用程序,使用 Python 语言

1
sudo vim app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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>"
<!-- more -->
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)

创建镜像指令文件 dockerfile

1
sudo vim dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Use an official Python runtime as a base 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 -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"]

指令语法:

FROM 表示从 python:2.7-slim 镜像基础上开始构建。这个镜像在哪里,docker hub 的仓库。 镜像命名规则 [镜像:版本]
WORKDIR 设置主进程的工作目录 /app
ADD 将当前目录内容复制到基础镜像文件系统的 /app
RUN 在基础镜像文件系统上运行 pip install -r requirements.txt 这时 pip 下载依赖包到基础镜像之上
EXPOSE 暴露容器对外的 TCP 端口 80
ENV 定义进程的环境变量 NAME World
CMD 设置主进程启动的命令 [“python”, “app.py”] 。因此,容器一般是单进程应用

创建 requirements.txt

1
sudo vim requirements.txt
1
2
Flask
Redis

构建 pythonweb 的镜像

检查是否已经创建了 dockerfile 、app.py 、requirements.txt 三个文件

1
ls

创建 tag 为 hello-python 的镜像:

1
docker build -t hello-python .

完成之后,检查镜像文件:

1
docker images

在容器中运行 app

1
docker run -p 4000:80 hello-python

docker run 运行镜像的命令
-d 后台运行
-p 端口映射,将容器的 80 端口映射到主机(host)的 4000 端口

检查结果:

也可以使用命令:

1
curl 192.168.56.100:4000

检查容器的进程:

1
2
3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab82c40651d5 hello-python "python app.py" 19 minutes ago Up 19 minutes 0.0.0.0:4000->80/tcp nervous_stonebraker

结束并清理进程:

1
docker stop ab82c40651d5

上传镜像到远程仓库

登录阿里云docker registry:

1
docker login --username=aidansu@163.com registry.cn-shenzhen.aliyuncs.com

将镜像推送到registry:

1
2
docker tag [ImageId] registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]
docker push registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:[镜像版本号]

其中[ImageId],[镜像版本号]请你根据自己的镜像信息进行填写。

使用图形化管理工具

启动图形化管理工具 Portainer

1
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

在浏览器输入 http://:9000 就进入了图形管理界面:

运行服务

运行一个服务,需要使用 docker-compose.yml 文件,这是服务组合的定义。

1、创建一个目录,例如 service_test:

1
cd && mkdir service_test && cd service_test

2、编写服务定义

1
sudo vim docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
# image: registry.cn-shenzhen.aliyuncs.com/aidansu/hello-python:1.0
image: <username/repository:tag>
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "80:80"
networks:
- webnet
networks:
webnet:

yaml 描述:

version = “3”;不能修改
services
web 服务名称
镜像: 请使用你仓库中的镜像
部署:申明使用 5 个容器实例,每个资源,重新启动条件
端口映射:80:80
网络: webnet
networks
webnet: 定义这个应用使用的 overlay 网络

3、启动服务

在启动服务之前,务必初始化集群模式 。注意:多网卡必须说明在那块网卡上组建集群。

1
docker swarm init --advertise-addr 192.168.56.100

检查是否在集群模式的命令:

1
docker info

启动应用程序服务:

1
docker stack deploy -c docker-compose.yml myservice

检查部署的结果:

1
docker stack ps myservice

使用浏览器或curl 访问 http://192.168.56.110:80/ 每次返回的 Hostname (主机名)都是不一样的,负载均衡实现了。

结束服务:

1
docker stack rm myservice

想了解更多关于 Docker 信息的可以查看最新的官方文档:https://docs.docker.com/

完!

坚持原创技术分享,您的支持将鼓励我继续创作!