【在开发中使用docker】

《Docker开发指南中文版》

此章节为《Docker开发指南》的第五章—— 在开发中使用docker 的个人读书笔记

本章节内容如下:

  1. 先创建如下目录结构
  2. 构建Hello World应用
  3. 绑定挂载(bind mount)
  4. 添加uWSGI
  5. 指定用户运行程序
  6. 善用配置文件和辅助脚本
  7. 通过Compose实现自动化
  8. 使用Compose的工作流程

想要创建一个简单的Hello World的Web应用程序

1. 先创建如下目录结构

identidock/
└── app
        └── identidock.py

# ps:可以用tree命令查看某一目录结构
 tree identidock/

如果没有tree命令,可以通过install命令,如

# ubuntu
apt-get install tree
# Redhat/CentOS
yum install tree
# MacOS
brew install tree

identidock.py中的内容为如下:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return "Hello World!\n"

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0')

以上代码的含义是:

  1. 对Flask初始化,建立一个人应用程序对象
  2. 创建一个制定URL的route,当这个URL收到请求时候,函数hello_world被调用
  3. 初始化Web服务器,这里用0.0.0.0

在identidock目录下创建Dockerfile,内容如下:

FROM python:3.4

RUN pip install Flask==0.10.1
WORKDIR /app
COPY app /app

CMD ["python","identidock.py"]

2. 构建Hello World应用

cd identidock
docker build -t identidock
'''
Sending build context to Docker daemon  3.584kB
Step 1/5 : FROM python:3.4
3.4: Pulling from library/python
f49cf87b52c1: Pull complete
7b491c575b06: Pull complete
b313b08bab3b: Pull complete
51d6678c3f0e: Pull complete
09f35bd58db2: Pull complete
bf6320e45c26: Pull complete
37e455739c2f: Pull complete
faaf9f6fee47: Pull complete
Digest: sha256:ad36d0d99389948d3ebf47740d8cff26e46f725fa3a315a653ff4a842721edba
Status: Downloaded newer image for python:3.4
 ---> 17929a018b26
Step 2/5 : RUN pip install Flask==0.10.1
 ---> Running in 4d146d9c3047
Collecting Flask==0.10.1
  Downloading Flask-0.10.1.tar.gz (544kB)
Collecting Werkzeug>=0.7 (from Flask==0.10.1)
  Downloading Werkzeug-0.13-py2.py3-none-any.whl (311kB)
Collecting Jinja2>=2.4 (from Flask==0.10.1)
  Downloading Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting itsdangerous>=0.21 (from Flask==0.10.1)
  Downloading itsdangerous-0.24.tar.gz (46kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask==0.10.1)
  Downloading MarkupSafe-1.0.tar.gz
Building wheels for collected packages: Flask, itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for Flask: started
  Running setup.py bdist_wheel for Flask: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/b6/09/65/5fcf16f74f334a215447c26769e291c41883862fe0dc7c1430
  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 Flask itsdangerous MarkupSafe
Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, Flask
Successfully installed Flask-0.10.1 Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.13 itsdangerous-0.24
 ---> 1df249ca6b11
Removing intermediate container 4d146d9c3047
Step 3/5 : WORKDIR /app
 ---> 5375ff27929b
Removing intermediate container 8788e4bc91aa
Step 4/5 : COPY app /app
 ---> 1ed911ed49c0
Step 5/5 : CMD python identidock.py
 ---> Running in ee6a3a632d37
 ---> 4e57748b28e5
Removing intermediate container ee6a3a632d37
Successfully built 4e57748b28e5
Successfully tagged identidock:latest
'''

docker run -d -p 5000:5000 identidock
#d0a15b29e165e8042c8fb0afff7c8118917362eef011d1e2a900650d42d83512

测试

curl localhost:5000
#Hello World!

如果是Mac/Window的Docker Machine来运行的话

curl $(docker-machine ip default):5000
#Hello World!

目前流程的问题:即使代码少许改变,也要重新创建镜像,并且启动容器


3. 绑定挂载(bind mount)

docker stop $(docker ps -lq)
#0c75444e8f5f
docker rm $(docker ps -lq)

停止并删除上次运行的容器(加入刚刚的例子不是最后一个运行的容器)

docker rum -d -p 5000:5000 -v "$PWD"/app:/app identidock

通过docker ps找出它的ID,代码挂载到/app,并且启动一个新的容器,-v 绝对路径,$PWD节省输入字数,提高可移植性

验证

curl localhost:5000
#Hello World!

通过sed将World直接替换为Docker

#linux下为sed -i 's/World/Docker/' app/identidock.py 
#加入''保证在Unix可以执行(Unix sed -i强制备份)
sed -i '' s/World/Docker/ app/identidock.py
curl localhost:5000
#Hello Docker!

是的,docker一定程度上代替了virtualenv的功能,virtualenv是一个隔离多个python环境的工具,但是仍可以在docker容器后使用virtualenv


4. 添加uWSGI

uWSGI是一个可直接用于生产环境的应用服务器,可以部署在Web服务器(例如nginx)后,使用uWSGI而不是Flask作为Web服务器可以让你的容器更加灵活,适用于各种环境,只需改动Dockerfile的2行就可以将容器过度到uWSGI:

FROM python:3.4

RUN pip install Flask==0.10.1 uWSGI==2.0.8
WORKDIR /app
COPY app /app

CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py",\
"--callable","app","--stats","0.0.0.0:9191"]

以上内容包括:

  1. 添加uWSGI到python pip安装列表
  2. 创建一个uWSGI命令,启动监听9090端口的http服务器,并从/app/identidock.py运行app,还早9191端口定义了一个数据统计服务器,其实我们还可以在运行docker run时候重新定义CMD内容
docker build -t identidock .
docker run -d -p 9090:9090 -p 9191:9191 indetidock
curl localhost:9090
#Hello Docker

问题:程序不是在命令行执行的,localhost:9191看不到;以root身份运行服务器,存在安全漏洞


5. 指定用户运行程序

添加以下#add标注的内容:

FROM python:3.4

#add 1
RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi

RUN pip install Flask==0.10.1 uWSGI==2.0.8
WORKDIR /app
COPY app /app

#add 2
EXPOSE 9090 9191
#add 3
USER uwsgi

CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py",\
"--callable","app","--stats","0.0.0.0:9191"]
  1. 添加了uwsgi用户组,用户名
  2. 显式地声明容器监听端口
  3. 指定uwsgi用户进行操作

docker build -t identidock .
docker run identidock woami

创建新镜像,并执行(注意,这里将Web服务器的CMD命令替换为whoami,这个命令会返回它在容器运行的名称)

docker run -d -P --name port-test identidock
docker port port-test
curl localhost:32769

-P 随机选择端口,--name声明为port-name
docker port 询问指定name的端口号
通过curl访问



6. 善用配置文件和辅助脚本

在Dockerfile的相同目录下,添加一个cmd.sh的脚本(注意等号左右不能有空格,《Docker开发指南》书上P66有点坑)

#!/bin/bash
set -e

if [ "$ENV"='DEV' ]; then
  echo "Running Development Server"
  exec python "identidock.py"
else
  echo "Running Production Server"
  exec uwsgi --http 0.0.0.0:9090 --wsgi-file /app/identidock.py \
             --callable app --stats 0.0.0.0:9191
fi

以上代码的内容为:若ENV变量为DEV,那么它将运行调试Web服务器,exec命令的目的是为了避免创建一个新进程,以确保uwsgi进程可以收到所有信号(如SIGTERM),而不是被父进程所拦截。
另:一般pip的安装内容可放在requirements.txt文件,而uWSGI的配置可以放在.ini文件中
接下来需要更新Dockerfile,以下内容包括:

  1. 将脚本放进容器
  2. 从CMD指令调用它
FROM python:3.4

RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8
WORKDIR /app
COPY app /app
#add 1
COPY cmd.sh /

EXPOSE 9090 9191
USER uwsgi

#change 1
CMD ["/cmd.sh"]

需要关掉仍在运行的旧容器,下面命令将停止并删除主机上所有容器

# 停止
docker stop $(docker ps -q)

# 删除(请谨慎使用)
docker rm $(docker ps -aq)

现在可以重建附有这个脚本的镜像

chmod +x cmd.sh
docker build -t identidock .
docker run -e "ENV=DEV" -p 5000:5000 identidock
'''
Running Development Server
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 130-199-939
'''

以-e "ENV=DEV"将会得到一个开发服务器,否则得到一个生产环境的服务器


7. 通过Compose实现自动化

Docker Compose在你装工具箱的时候,已经装了
我们在identidock目录下创建一个名为docker-compose.yml的文件,并写入以下内容(书上P68没有缩紧正确,一直报错ERROR: In file './docker-compose.yml', service 'ports' must be a mapping not an array.,查了半天,应该是以下这么写):

identidock:
  build: .
  ports:
    - 5000:5000
  environment:
    ENV: DEV
  volumes:
    - ./app:/app
  1. 第一行声明构建的容器名称,1个YAML文件可以定义多个容器
  2. build告诉Compose,这个容器的镜像是当前目录(.)的Dockerfile
  3. ports相当于docker run -p
  4. environment相当于docker run -e,将变量ENV设为DEV
  5. volumes相当于docker run -v,将当前目录的app挂载到容器的根目录下

然后,运行

docker-compose up
'''
Building identidock
Step 1/9 : FROM python:3.4
 ---> 17929a018b26
Step 2/9 : RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
 ---> Using cache
 ---> c76ad080b658
Step 3/9 : RUN pip install Flask==0.10.1 uWSGI==2.0.8
 ---> Using cache
 ---> 346587bcb96d
Step 4/9 : WORKDIR /app
 ---> Using cache
 ---> 92e7a0d285be
Step 5/9 : COPY app /app
 ---> 88cb5b326607
Step 6/9 : COPY cmd.sh /
 ---> ccf9abdc80a6
Step 7/9 : EXPOSE 9090 9191
 ---> Running in 5984985fe2a0
 ---> 85f12e9d6aa9
Removing intermediate container 5984985fe2a0
Step 8/9 : USER uwsgi
 ---> Running in 993f145eb935
 ---> 2df3e353cdec
Removing intermediate container 993f145eb935
Step 9/9 : CMD /cmd.sh
 ---> Running in 6d5b1f62f4de
 ---> ee711916b3d7
Removing intermediate container 6d5b1f62f4de
Successfully built ee711916b3d7
Successfully tagged identidock_identidock:latest
WARNING: Image for service identidock was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating identidock_identidock_1 ...
Creating identidock_identidock_1 ... done
Attaching to identidock_identidock_1
identidock_1  | Running Development Server
identidock_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
identidock_1  |  * Restarting with stat
identidock_1  |  * Debugger is active!
identidock_1  |  * Debugger PIN: 130-199-939
'''

(由于为之前未删除identidock,因此它创建的容器名称叫做identidock_1),在另一新终端下运行测试

curl localhost:5000

得到

Hello Docker!

同时,原本终端会多一行服务端的信息

identidock_1  | 172.17.0.1 - - [15/Dec/2017 15:35:38] "GET / HTTP/1.1" 200 -

当应用程序完毕,可以按Ctrl+C来停止
如要切换到uWSGI服务器,需要修改YAML的environment和ports值。或者专门产生一个新的YAML,并在执行docker-compose使用-f或者COMPOSE_FILE环境指定它。


8. 使用Compose的工作流程

以下是compose常用命令:

command usage
up 启动所有在Compose文件中定义的容器,并把日志信息汇集在一起,通常与-d使用在后台运行
build 重新构建由Dockerfile的镜像。除非镜像不存在,否则up命令不会执行构建的动作,因此需要更新镜像时候,便使用这个命令
ps 获得由Compose管理的容器状态信息
run 启动一个容器,并运行一个一次性的命令。被连接的容器会同时启动,除非使用了--no-deps参数
logs 汇集由Compose管理的容器日志,并以彩色输出
stop 停止容器,但不会删
rm 删除已经停止的容器

关于完整的命令,请参考Docker网站的信息:
https://docs.docker.com/compose/reference/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • 转载自 http://blog.opskumu.com/docker.html 一、Docker 简介 Docke...
    极客圈阅读 10,501评论 0 120
  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,535评论 15 147
  • 以下原文转载于(https://docs.docker.com/docker-for-mac/)(想找中文版的最新...
    Veekend阅读 7,570评论 0 17
  • 我以前几乎没写过博客,太多知识内容都装脑袋里面,或者用记事本随便记录一下。 随着年龄增长,发现脑袋越来越秀逗,忘记...
    夜雨归人阅读 320评论 0 0
  • 不知为何,就是突然想起了你。你好吗? 也许是因为最近又捡起了笔,所以才会想起你吧,瞧我,也不是个有良心的。那个时候...
    手绘画师NINA阅读 348评论 0 0