Docker应用实践&一些使用技巧
使用docker的理由:
-
不污染主系统就可以使用众多Linux程序(替代虚拟化技术)
-
保证项目运行环境一致(运行环境可加入版本控制,做成镜像更容易重复使用)
-
保证项目各个部分在一个封闭的空间内,不受外交干扰(微服务化)
开发中的使用技巧
OVERRIDE文件的使用
docker-compose.override.yml文件里的内容会覆盖docker-compose.yml的内容,可以在里面定义本地环境自有的属性,还可以在里面添加service,这样本地的服务也可以使用同一个网络环境了。比如原docker-compose里没有mysql服务,可以在override里面加一个,还比如一些需要绑定本地目录的由于本地路径无法确定,需要写在override里面;如果一组service里面你只想启用部分,可以在override中用image:hello-world;entrypoint:[true]来覆盖不想启动的service
使用npm环境编译vue项目
docker run -it -d -v:~/code:/www -w /www node bash
exec进入容器后就可以使用npm命令了
删除名称为none的镜像
docker rmi $(docker images | grep "none" | awk '{print $3}')
docker run 的时候加上--rm可以避免生成名称为none的镜像
固定容器IP
每个docker-compose启动的时候会生成一个名称为default的network,通过配置network可以固化容器的IP
例:php代码里面要调用www.site1.com这个站点的服务,这个站点运行在nginx服务下,需要配置hosts才能访问;如果不固化ip,每次重启服务都可能引起nginx服务IP变化,导致hosts实效。固化IP的方法如下:
version: '3'
services:
nginx:
networks:
default:
ipv4_address: 172.20.1.13
depends_on:
- php
php:
extra_hosts:
- "www.site1.com:172.20.1.13"
networks:
default:
ipv4_address: 172.20.1.14
networks:
default:
ipam:
config:
- subnet: 172.20.1.0/24
注意:这里固定的ip只是SERVER_IP,REMOTE_ADDR获取到的是网关的IP
address不能设为.1,.1默认被网关占用了
访问宿主机的服务
一般推荐需要相互访问的服务写在一个docker-compose中,这样这些服务是在同一个网络中的可以直接访问。如果实在要访问不同的容器提供的服务,通过暴露端口绑定宿主机端口来实现。!!注意宿主机的防火墙设置!!容器中是可以访问宿主机的本地IP+端口的。
建本地私有仓库
registry 官方开源的仓库代码
https://www.jianshu.com/p/fef890c4d1c2
项目部署实践和遇到的问题
项目部署推荐使用docker-compose编排容器。
-
项目代码目录权限问题
使用volume方式绑定的项目文件会存在权限问题(项目文件在容器内可能没有执行权限)
解决方式:通过dockerfile的COPY命令把项目文件拷贝到容器内,拷贝进去的文件默认是root权限,COPY命令也有 --chown 参数可以设置文件权限,但仅支持linux下。也可以在dockerfile内通过chown、chmod命令给文件授权;COPY和ADD的区别。推荐使用COPY
-
上下文(context)概念
-
多个服务部署同一台机器的问题
宿主机起一个nginx服务,docker的nginx服务映射到本地的非80端口,然后通过宿主机的nginx的proxy服务转发到docker内nginx服务绑定的本地端口上。
-
生产环境和和测试环境不用env文件的维护
部署的项目在git上使用dev,release分支来区分env文件,见下面的例子,不同分支上的env文件是不一样的。
目前项目中遇到的相对最复杂的docker-compose案例
image.pngdocker-compose.yml文件:
version: '3'
services:
nginx:
image: nginx:latest
tty: true
volumes://项目代码放在同级目录中
- ../site-group/nginx.template:/etc/nginx/conf.d/nginx.template:ro
- ../site-group:/www:ro
environment:
- ADMIN_HOST=site-group.local
command: ["bash", "/www/nginx-up.sh"] //替换nginx.template中的ADMIN_HOST环境变量的脚本
depends_on: //效果是先启动php再启动nginx
- php
php:
build: //生产环境用Dockerfile中的ADD命令来添加项目代码,可以解决文件权限问题,
开发环境可以直接在下面volumes中绑定项目代码,然后手动修改权限(ADD要时间)
context: ../site-group
dockerfile: Dockerfile
tty: true
working_dir: /www
volumes:
- ./site-group/.env:/www/.env //使用目录中的env文件替换项目中的env文件
command: ["bash", "/www/php-up.sh"] //项目启动时运行的脚本
mysql:
image: mysql:5.7.22
environment:
- MYSQL_ROOT_PASSWORD=root
site-group-spider:
build: //一个python程序,从dockerfile构建
context: ../site-group-spider
dockerfile: Dockerfile
volumes:
- ../site-group-spider:/app
- ./site-group-spider/config.py:/app/config.py //替换配置文件
tty: true
entrypoint: ["python", "/app/spider/spider.py"] //替换entrypoint
docker-compose.override.yml文件:
version: '3'
services:
out-nginx: //外层的nginx服务,用来模拟宿主机的nginx服务
image: nginx:latest
volumes:
- ../site-group/out_admin_nginx.conf:/etc/nginx/conf.d/default.conf
- ../site-group/out_nginx.conf:/etc/nginx/conf.d/out_nginx.conf
- ../site-group/inotify:/www/inotify
ports:
- "80:80"
depends_on:
- php
- nginx
networks:
default:
ipv4_address: 172.20.1.11 //固化容器IP
site-group-spider:
image: hello-world
entrypoint: ["true"]
networks:
default:
ipv4_address: 172.20.1.12
nginx:
ports:
- "8881:80"
environment: //替换env变量为本地值
- ADMIN_HOST=www.site-group.com
networks:
default:
ipv4_address: 172.20.1.13
depends_on:
- php
mysql:
ports:
- "3306:3306"
volumes:
- ~/docker/mysql/backup:/backup
- ~/docker/mysql/data:/var/lib/mysql
php:
extra_hosts: //配置hosts,因为php服务中要调用nginx服务提供的站点,不配置hosts无法访问到
- "www.site1.com:172.20.1.13"
networks:
default:
ipv4_address: 172.20.1.14
networks:
default:
ipam:
config:
- subnet: 172.20.1.0/24
site-group项目中的文件:
Dockerfile:
FROM panwenbin/php-fpm-7.1-laravel-centos
ADD . /www //使用ADD方式添加项目代码到容器内
WORKDIR /www
RUN composer install --no-dev \ //执行安装vendor目录
&& chown -R apache:apache /www //修改项目目录所有者,和php服务用户一致,就可以解决权限问题
EXPOSE 9000
nginx.template:nginx容器使用的配置文件模板
server {
listen 80;
server_name _; //不限制域名
client_max_body_size 100m;
root /www/public;
index index.html index.php index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
#根据域名生成数据表前缀
if ( $host ~ ^www\.(\w+)\.(\w+)$ ) {
set $prefix $1_$2_;
}
if ( $host ~ ^www\.(\w+)\.(\w+)\.(\w+)$ ) {
set $prefix $1_$2_$3_;
}
if ( $host != '${ADMIN_HOST}' ) { //此处的环境变量为docker-compose文件中配置的值
set $dbPrefix $prefix;
}
error_page 404 /index.php;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
fastcgi_pass php:9000; //php服务名称和暴露的端口
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_param DB_PREFIX $dbPrefix;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
nginx-up.sh:替换环境变量的脚本
#!/bin/bash
envsubst '${ADMIN_HOST}' < /etc/nginx/conf.d/nginx.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'
out_admin_nginx_conf.example:外部(宿主机)nginx服务使用的nginx配置,做了一层转发到内部的nginx服务,按注释要求修改后使用
server {
listen 80;
#系统后台域名
server_name site-group.com;
return 301 http://www.$host$request_uri;
}
server {
listen 80;
#系统后台www域名
server_name www.site-group.com;
location = / {
rewrite / /admin redirect;
}
location / {
#转发到内部的nginx开放的端口
proxy_pass //本机IP+暴露端口
proxy_set_header Host $host;
}
}
out_nginx_conf.example:同上
server {
listen 80;
#路径替换为项目路径
include /www/inotify/nginx_server_names.conf; //inotify文件中生成的存放servername的文件,通过inotify服务监听自动重启外层的nginx服务
return 301 http://www.$host$request_uri;
}
server {
listen 80;
#路径替换为项目路径
include /www/inotify/nginx_www_server_names.conf;
location / {
#转发到docker的nginx开放的端口
proxy_pass http://192.168.2.75:8881;
proxy_set_header Host $host;
}
}
php-up.sh
#!/bin/bash
php artisan nginx:up-servernames && php-fpm -F