转载请注明原始出处:http://blog.csdn.net/a464057216/article/details/71122956
后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
镜像和容器
镜像是可执行的、独立的、轻量包,包含了软件运行需要的一切,比如代码、库、环境变量和配置文件等。镜像没有状态也不会改变。
容器是镜像在内存中的一个运行实例,容器的运行环境与宿主环境完全隔离,只会访问宿主的端口或文件。容器在宿主机的内核上像普通程序一样运行在单独的进程中,没有额外的内存开销,虚拟机中的程序只能通过hypervisor获得宿主资源的虚拟访问权限。容器的性能比虚拟机更好。
通过Docker引擎,用户不必担心自己的电脑是否可以运行Docker镜像中的程序,Docker容器总是可以运行的。
上一篇博文中,通过启动hello-world
程序验证Docker是否成功安装时,docker run hello-world
中的run
是一个创建和运行容器的子命令,hello-world
告诉Docker加载哪个镜像到容器中。
使用容器
以Python应用开发为例,通常希望产品机器和开发机器使用相同的Python运行环境,借助Docker可以将可执行的Python运行环境封装在容器中,这样同一个程序无论在哪里都使用同样的Python运行环境。Dockerfile
定义容器内的环境发生了什么,访问文件系统、网络接口等都是与宿主机隔离的,所以需要定义将外界的哪些文件拷贝到容器环境中、容器的端口与宿主机端口的映射关系等。
# 使用官方的Python 2.7-slim作为基准镜像
FROM python:2.7-slim
# 创建目录/app并切换当前工作目录到该目录(构建容器时,默认以root身份进行)
WORKDIR /app
# 将Dockerfile所在目录的内容拷贝至/app
ADD . /app
# 使用pip安装requirements.txt中的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 容器开放80端口给外界
EXPOSE 80
# 定义环境变量NAME
ENV NAME World
# 容器启动时通过python app.py启动应用程序
CMD ["python", "app.py"]
以下是requirements.txt
中的内容:
Flask
Redis
以下是app.py
中的内容:
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# 连接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', "mars"),
hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
因为Dockerfile
中没有安装Redis,所以这个程序访问Redis会报错。第7行初始化redis对象时,host
参数给定的redis
表示域名。将Dockerfile
、requirements.txt
、app.py
放在同一目录,构建容器。-t
参数表示为镜像设置名字及标记,遵循name:tag
的格式;如果只提供一个字符串,则赋值给name
参数,tag取默认的latest
;如果未提供-t
参数,则name和tag默认为<none>
(注意命令最后的点):
# Written by: CSDN - Mars Loo的博客
$ docker build -t friendlyhello:1 .
Sending build context to Docker daemon 4.608 kB
Step 1/7 : FROM python:2.7-slim
2.7-slim: Pulling from library/python
cd0a524342ef: Pull complete
34a63ab31b00: Pull complete
d9acb0d1bdb2: Pull complete
Digest: sha256:b5ae405a173110610c594035b5cae5e39a6f305b8e94fb116e9b5a5434412f30
Status: Downloaded newer image for python:2.7-slim
---> 7fd4e5a52ace
Step 2/7 : WORKDIR /app
---> 6e604ee6a4bb
Removing intermediate container 18b4e7ad0534
Step 3/7 : ADD . /app
---> a10207cfd577
Removing intermediate container 4a9bdc498ad1
Step 4/7 : RUN pip install -r requirements.txt
---> Running in 54e7c89c18d6
Collecting Flask (from -r requirements.txt (line 1))
Downloading Flask-0.12.1-py2.py3-none-any.whl (82kB)
Collecting Redis (from -r requirements.txt (line 2))
Downloading redis-2.10.5-py2.py3-none-any.whl (60kB)
Collecting itsdangerous>=0.21 (from Flask->-r requirements.txt (line 1))
Downloading itsdangerous-0.24.tar.gz (46kB)
Collecting Jinja2>=2.4 (from Flask->-r requirements.txt (line 1))
Downloading Jinja2-2.9.6-py2.py3-none-any.whl (340kB)
Collecting Werkzeug>=0.7 (from Flask->-r requirements.txt (line 1))
Downloading Werkzeug-0.12.1-py2.py3-none-any.whl (312kB)
Collecting click>=2.0 (from Flask->-r requirements.txt (line 1))
Downloading click-6.7-py2.py3-none-any.whl (71kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask->-r requirements.txt (line 1))
Downloading MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
Running setup.py bdist_wheel for itsdangerous: started
Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
Stored in directory: /root/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129a
Running setup.py bdist_wheel for MarkupSafe: started
Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
Stored in directory: /root/.cache/pip/wheels/88/a7/30/e39a54a87bcbe25308fa3ca64e8ddc75d9b3e5afa21ee32d57
Successfully built itsdangerous MarkupSafe
Installing collected packages: itsdangerous, MarkupSafe, Jinja2, Werkzeug, click, Flask, Redis
Successfully installed Flask-0.12.1 Jinja2-2.9.6 MarkupSafe-1.0 Redis-2.10.5 Werkzeug-0.12.1 click-6.7 itsdangerous-0.24
---> bfcccbceca04
Removing intermediate container 54e7c89c18d6
Step 5/7 : EXPOSE 80
---> Running in 18af59dc08dc
---> 2fe58198cdf7
Removing intermediate container 18af59dc08dc
Step 6/7 : ENV NAME World
---> Running in 09cc56ac5c82
---> 615fcebbb612
Removing intermediate container 09cc56ac5c82
Step 7/7 : CMD python app.py
---> Running in 2e338caa34cc
---> 7ce17a909fd5
Removing intermediate container 2e338caa34cc
Successfully built 7ce17a909fd5
通过docker images
可以查看本地镜像列表:
# Written by: CSDN - Mars Loo的博客
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello 1 7ce17a909fd5 About a minute ago 194 MB
python 2.7-slim 7fd4e5a52ace 5 days ago 182 MB
启动容器,将宿主机的8400端口映射到容器的80端口:
$ docker run -p 8400:80 friendlyhello:1
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
浏览器访问宿主机的8400端口可以看到容器内获取hostname
得到的是容器ID:
docker ps
可以查看容器ID:
# Written by: CSDN - Mars Loo的博客
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4bfc02df983 friendlyhello:1 "python app.py" 22 seconds ago Up 20 seconds 0.0.0.0:8400->80/tcp eloquent_stallman
Ctrl
+C
退出容器应用,使用-d
参数后台运行容器,将打印完整的容器ID后退出:
# Written by: CSDN - Mars Loo的博客
$ docker run -dp 8400:80 friendlyhello:1
e25f1119fb394b0271e72ba9da322f72d5e97a35b7189467e75e8ab3f400712a
使用docker stop
可以停止该程序:
# Written by: CSDN - Mars Loo的博客
$ docker stop e25f1119fb39
e25f1119fb39
如果容器无法被正常停止,可以使用docker kill
强制停止:
# Written by: CSDN - Mars Loo的博客
$ docker kill cbf1d0f24414
cbf1d0f24414
如果使用
netstat
观察宿主机的8400端口,监听的是TCP6即ipv6协议的端口,但其实仍然是可以使用ipv4协议访问的:
# Written by: CSDN - Mars Loo的博客
$ sudo netstat -nlp | grep 8400
tcp6 0 0 :::8400 :::* LISTEN 53408/docker-proxy
共享镜像给他人
共享制作好的镜像给他人,类似共享写好的代码给他人。比如本地commit之后,push到远端的代码托管平台(GitHub或者自建的GitLab平台)。Docker的镜像托管平台称为Docker Registry,默认的Registry是国外的Docker Hub,如果被墙,可以使用国内的https://c.163.com,或者为了产品的安全性自己搭建Docker Registry。
首先需要登录至相应的Docker Registry,docker login
命令后的参数表示登录地址,比如:
# Written by: CSDN - Mars Loo的博客
$ docker login hub.c.163.com
Username: marsloo
Password:
Login Succeeded
为本地镜像打标签,标签相当于建立本地镜像和远程镜像的关联,格式为username/repository:tag
,即用户username
名下的repository
仓库的tag
版本。Docker Registry是仓库的集合,仓库是镜像的集合:
$ docker tag friendlyhello:1 hub.c.163.com/learndocker/learndocker:v1
镜像打完标签后,如果使用docker images
命令查看,会有两个名字不同但image id
相同的镜像。
上传打过标签的镜像到Docker Registry:
$ docker push hub.c.163.com/learndocker/learndocker:v1
上传完成后镜像就是公开的了,先删除本地的镜像:
$ docker rmi hub.c.163.com/learndocker/learndocker:v1
$ docker rmi -f friendlyhello:1
$ sudo rm -rf /var/lib/docker
镜像已经共享到公开的Docker Registry,此时在任何装有Docker的机器上都可以运行该镜像了:
$ docker run -p 8400:80 hub.c.163.com/learndocker/learndocker:v1
如果在构建和运行镜像时没有指定冒号后面的tag名,则系统使用默认的latest
。
接下来推荐阅读通过服务和集群的方式使用Docker,可以方便进行程序规模的扩展和流量的负载均衡。
如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!