Dockerfile文件教程
Dockerfile
创建Dockerfile文件
mkdir dockerfiler && cd dockerfiler
vim Dockerfile
from ubuntu # 来自于哪个镜像
label maintainer="xionglilong" # 维护者
run sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list # 修改为国内源
run apt-get update # 更新软件缓存
run apt-get install -y nginx # 静默安装nginx
copy index.html /var/www/html # 拷贝当前目录index.html文件到容器/var/www/html目录
entrypoint ["/usr/sbin/nginx","-g","daemon off;"] # 入点命令,前台执行
expose 80 # 暴露80端口
镜像分成:Dockerfile文件中每运行一行命令就产生一层
run命令注意:为了避免无用分层,合并多条命令成一行
其他选项:
命令 | 解释 | 例子 | 注意 |
---|---|---|---|
add | 和copy类似,不过可以添加远程文件和解压文件 | add test.tar.gz / | 大部分情况使用copy 添加远程文件/目录请使用 curl 或wget add 的src可以是压缩文件(tar、zip、tgz等),会被自动解压到镜像目标路径,无需手动解压 |
workdir | 指定当前工作目录。如果没有会自动创建 | workdir /root workdir demo | 使用workdir 代替run cd 在使用 workdir 时尽量使用绝对目录 |
env | 设定环境变量 | 尽量使用env 增加可维护性。env变量可以在构建期间被使用(通过$ 访问),也可以在最终镜像启动的容器内访问。 | |
user | 指定用户 | ||
volume | mount point | ||
expose | 暴露端口,可以被其他容器访问 | 仅是可以被其他link的container访问到,不代表可以被外网访问。run -p 向外网暴露端口时,会自动进行expose。 | |
run | 执行命令并创建新的Image layer | ||
cmd | 设置容器启动后缺省执行的命令和参数 | 该命令会在容器启动且 docker run 后面没有指定其他命令时执行 1. 如果在容器启动使用docker run指定其他命令,cmd命令被忽略 2. 如果定义了多个cmd,只有最后一个生效 | |
entrypoint | 设置容器启动时运行的命令 | 让容器以服务的形式运行 不会被忽略,一定会执行 可以有多个 ENTRYPOINT 指令,也是只有最后一个生效 与 CMD 不同的是,CMD 或 docker run 之后的参数会被当作参数传给 ENTRYPOINT |
cmd与entrypoint选择
如果运行的类似mysql容器,优先使用entrypoint指令,因为cmd可以为entrypoint提供默认参数,同时docker run ...
时也可替换默认cmd参数。
如果只是简单的设置容器默认的启动命令,使用cmd即可,用户只需要docker run ...
后添加参数替换掉默认值即可
运行Dockerfile文件
# 在当前目录下构建"Dockerfile"文件为"xionglilong/helloworld"的镜像
# docker image build (简写)
docker build -t xionglilong/helloworld .
#将当前目录的Dockerfile文件运行,创建一个hello_docker镜像
docker -t xionglilong/hello_docker .
-t:设置镜像的tag
build
命令是将Dockerfile
--->image
部署django项目和gunicorn服务器
# 建立python3.10环境
FROM python:3.10
# 设置python环境变量
ENV PYTHONUNBUFFERED 1
# 启动端口
ENV PROJECT_PORT 8028
# 数据库环境变量
ENV DATABASE_HOST pgm-bp180ga270wm4qn7uo.pg.rds.aliyuncs.com
# OSS环境变量
ENV OSS_REGION https://oss-cn-hangzhou.aliyuncs.com
# 在容器内创建项目根目录
RUN mkdir -p /var/www/donfeng-weighting-django
# 设置容器内工作目录,类似于cd命令
WORKDIR /var/www/dongfeng-weighting-django
# 复制当期目录文件到容器项目根目录
ADD . /var/www/dongfeng-weighting-django/
# 安装pipenv
RUN pip install pipenv -i https://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple
# 生成导出requirements.txt文件
RUN pipenv requirements > requirements.txt
# 安装项目依赖包
RUN pip install -r requirements.txt -i https://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple
# 安装gunicorn
RUN pip install gunicorn gevent -i https://pypi.douban.com/simple/ --trusted-host=pypi.douban.com/simple
# 测试使用
CMD python manage.py runserver 0:${PROJECT_PORT}
CMD gunicorn --worker-class=gevent --worker-connections=1000 --workers=$((`grep processor /proc/cpuinfo | wc -l`*2+1)) --worker-tmp-dir /dev/shm -b 0.0.0.0:${PROJECT_PORT} settings.wsgi
gunicorn命令详解
反引号
的内容是子命令的结果,获取CPU核心数量。$(())
是实现计算功能,将cpu核心数乘以2后加1。--worker-tmp-dir
是修改临时目录,因为docker的/tmp目录实际硬盘空间,而不是内存空间。${}
是上面ENV指定的变量。ENV创建的变量既可以在该文件内用,也可以在生成镜像后创建的容器内使用。
最后构建镜像和创建容器
# 构建镜像
docker build -t dongfeng-weighting-django .
# 创建容器
docker run -d --name=dongfeng-weighting-django -p 8028:8028 dongfeng-weighting-django
部署一个python flask后台应用示例
目录结构
flask-helloworld/
|
|—— app.py
|__ Dockerfile
flask应用源码
>>> vim app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "hello docker"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
Dockerfile文件内容
from python:3.6
label maintainer="Xiong Lilong<xionglilong@163.com>" # 注释
run pip install flask
copy app.py /app/ # 将app.py文件拷贝到容器的新目录app下面
workdir /app # 切换工作目录
expose 5000
cmd ["python", "app.py"]
通过Dockerfile文件构建镜像
cd flask-helloworld
docker build -t xionglilong/python3.6-helloworld .
运行该镜像
docker run -d --name helloworld -p 8080:80 xionglilong/python3.6-helloworld
-d:让程序在后台运行
部署flask和redis多容器示例
目录结构
flask-redis/
|
|—— app.py
|—— Dockerfile
|__ requirements.txt
flask应用源码
>>> vim app.py
import os
import socket
from flask import Flask
from redis import Redis
app = Flask(__name__)
# 通过容器主机REDIS_HOST环境变量获取redis域名,缺省为127.0.0.1
redis = Redis(host=os.environ.get("REDIS_HOST", "127.0.0.1"), port=6379)
@app.route('/')
def hello(): # 处理网站根目录的视图函数
redis.incr('hits') # 每次访问加1
return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get("hits"), socket.gethostname())
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
Dockerfile文件内容
from python:3.6
label maintainer="Xiong Lilong<xionglilong@163.com>" # 注释
copy . /app # 将app.py文件拷贝到容器的新目录app下面
workdir /app # 切换工作目录
run pip install flask redis
expose 5000 # web的端口
cmd ["python", "app.py"]
创建redis容器
# redis不提供对外访问,所以不使用-p端口映射
docker run -d --name redis redis
通过Dockerfile文件构建镜像
cd flask-redis
docker build -t xionglilong/flask-redis .
运行该镜像
docker run -d -p 5000:5000 --link redis --name flask-redis -e REDIS_HOST=redis xionglilong/flask-redis
docker exec -it flask-redis /bin/bash # 进入容器
env | grep REDIS_HOST # 查看REDIS_HOST环境变量是否设置
curl 127.0.0.1:5000 # 测试
--link redis:通过名字连接到redis容器,可以通过redis名访问redis容器
-e REDIS_HOST=redis:在该容器内设置环境变量
多机器的容器通过vxlan通信示例(未通过)
使用etcd
开源的分布式存储
yum install etcd
在docker-node1(dongfe.com)主机上:
nohup etcd --name docker-node1 \
--initial-advertise-peer-urls http://172.17.229.230:2380 \
--listen-peer-urls http://172.17.229.230:2380 \
--listen-client-urls http://172.17.229.230:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://172.17.229.230:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://172.17.229.230:2380,docker-node2=http://www.shenjidaili.com:2380 \
--initial-cluster-state new&
在docker-node2(shenjidaili.com)主机上:
nohup etcd --name docker-node2 \
--initial-advertise-peer-urls http://192.168.0.210:2380 \
--listen-peer-urls http://192.168.0.210:2380 \
--listen-client-urls http://192.168.0.210:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.0.210:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://dongfe.com:2380,docker-node2=http://192.168.0.210:2380 \
--initial-cluster-state new&
检查cluster状态
etcdctl cluster-health
重启docker服务
在docker-node1上:
service docker stop
/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://172.17.229.230:2379 --cluster-advertise=172.17.229.230:2375&
在docker-node2上:
service docker stop
/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.0.210:2379 --cluster-advertise=192.168.0.210:2375&
部署一个测试系统性能命令行工具的示例
docker run -it ubuntu
apt-get update
apt-get install -y stress
stress --vm 1 --verbose
Dockerfile文件内容
>>> vim ubuntu-stress/Dockerfile
from ubuntu
run apt-get update && apt-get install -y stress
# entrypoint + cmd 接收参数
entrypoint ["/usr/bin/stress"]
cmd []
构建和运行镜像
# 构建镜像
docker build -t xionglilong/ubuntu-stress .
# 运行镜像(后面的参数是应用接收的参数)
docker run -it xionglilong/ubuntu-stress --vm 1 --verbose
数据持久化
Data Volume数据持久化
提供独立于容器之外的持久化存储
docker卷保存在本地的/var/lib/docker/volumes/目录下
# 将容器内的/var/lib/mysql目录持久化并命名为'mysql'卷
docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 将容器内/usr/share/nginx/html目录持久化
docker run -d --name hello-nginx -v /usr/share/nginx/html nginx
-v VOLUME NAME:CONTAINER URL
:将容器某url目录持久化并命名为VOLUME NAME卷
docker volume ls # 查看卷
docker volume inspect [VOLUME NAME] # 视察卷
docker volume rm [VOLUME NAME] # 删除卷
# 检查容器数据,查看"Mounts"配置项
docker inspect hello-nginx
Bind Mounting数据持久化
将主机目录挂载到容器目录上,可以作为开发环境使用(DevOps的第一步)
#将主机当前目录下的html目录映射到容器中/usr/share/nginx/html目录上
docker run -d -p 80:80 -v $PWD/html:/usr/share/nginx/html nginx
docker run -d -p 80:80 -v $(pwd)/html:/usr/share/nginx/html nginx
-v HOST URL:CONTAINER URL
:将主机目录挂载到容器目录
容器之间共享目录卷
docker run -it --volumes-from data_container ubuntu /bin/bash
# 查看挂载
mount