基于Docker统一PHP开发环境

背景

  • 团队内每个开发人员PHP版本不一致
  • 本地开发环境与生产环境不一致,环境不一致易导致生产问题
  • 每次新人过来安装PHP环境耗时比较长且不可控

目标

  • 所有开发人员环境保持一致
  • 开发环境与生产环境保持一致
  • 能快速安装轻量级的开发环境

思路

  • 将标准PHP环境打包静docker镜像中,所有开发成员代码运行在容器里面,本地不需要安装开发环境(除IDE工具外)
  • 容器里面提供sshd服务,使开发者能够通过ssh登陆并传输文件(类似轻量级虚拟机,在容器启动后利用sftp远程同步文件比较有用)

构造环境镜像

  • 版本
    linux:alpine3.13
    PHP: 7.4
  • 准备文件(在后面国建镜像有用)

nginx主配置文件:nginx.conf

user                nginx;
worker_processes    auto;
error_log           /var/log/nginx/error.log notice;
pid                 /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    sendfile            on;
    keepalive_timeout   65;
    gzip                on;
    #tcp_nopush         on;
    log_format          main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    access_log          /var/log/nginx/access.log  main; 

    include /etc/nginx/conf.d/*.conf;
}

镜像入口文件(用于启动镜像服务): entrypoint.sh

#!/bin/sh

/usr/sbin/sshd -D &
nginx &
php-fpm
  • 镜像Dockerfile
    在有了上面两个文件后,我们就可以开始构建我们的环境镜像了
# 采用基于alpine的php-fpm镜像(alpine为一个轻量级的linux镜像)
FROM amd64/php:7.4-fpm-alpine3.13

# 复制PHP配置文件
RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# 由于仓库镜像网络问题,换成阿里云资源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

# 安装php扩展(可以根据自己的需要安装所需PHP扩展)
# install-php-extensions使用文档:
# https://github.com/mlocati/docker-php-extension-installer
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions
RUN install-php-extensions swoole
RUN install-php-extensions mongodb
RUN install-php-extensions redis
RUN install-php-extensions rdkafka
RUN install-php-extensions bcmath
RUN install-php-extensions pdo_mysql

# 安装composer
RUN php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');" 
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# alpine利用apk进行系统包管理
# 更新apk库并安装时区数据库、sshd服务、nginx
RUN apk update && apk add tzdata openssh nginx

# 配置时区(alpine默认时区为0时区,与北京时间相差8小时,需要调整为Asia/Shanghai)
ENV TIME_ZONE=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone


# 安装openssh,容器启动后可以通过ssh登陆或传输文件
# 初始账号密码为root:123456
RUN sed -i "s/#PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config &&\
    ssh-keygen -t dsa -P "" -f /etc/ssh/ssh_host_dsa_key &&\
    ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key &&\
    ssh-keygen -t ecdsa -P "" -f /etc/ssh/ssh_host_ecdsa_key &&\
    ssh-keygen -t ed25519 -P "" -f /etc/ssh/ssh_host_ed25519_key &&\
    echo "root:123456" | chpasswd


# 配置nginx主配置文件
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /data/entrypoint.sh

# 安装swoft开发工具(swoft框架的开发工具,如果没有用到则不需要安装)
RUN wget https://github.com/swoft-cloud/swoft-cli/releases/download/v0.2.1/swoftcli.phar &&\
        mv swoftcli.phar /usr/local/bin/swoftcli &&\
        chmod a+x /usr/local/bin/swoftcli


# 设置默认工作目录
WORKDIR /data/web/

# 暴露端口
EXPOSE 22
EXPOSE 80
EXPOSE 9000

# 启动sshd、nginx、fpm
CMD ["sh","/data/entrypoint.sh"]

构建镜像

docker build -t jeanslin/php7.4-dev . 

查看构建后的镜像


image.png

运行环境

  • 启动容器
    此处可以将存放代码的目录挂载到容器里面,这样宿主机和容器的代码就是相同的
docker run -d 
    -p 10080:80 -p 10022:22 -p 19000:9000 
    -v /Users/it/project:/data/web/project
    jeanslin/php7.4-dev
  • 利用ssh登陆容器
    由于我们把容器的22端口映射到宿主机的10022端口,通过10022端口就可以登陆容器里面


    image.png

查看已启动的服务进程,可以看到已启动了php-fpm、nginx、sshd服务


image.png
  • 配置后端nginx文件:api.conf
    由于容器只对外开放了80端口,为了区分不同的服务,我们可以在宿主机/etc/hosts文件配置开发域名来映射到容器的不同服务,此处将api.dev.com映射到127.0.0.1
server {
        listen       80;                                       
        server_name  api.dev.com;
        root         /data/web/;

        location / {
            index index.php index.html index.htm;
            try_files $uri $uri/ /index.php$is_args$args;
        }
        
        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param SERVER_NAME $http_host;
            fastcgi_ignore_client_abort on;
        }
}

我们在容器的/data/web/写一个phpinfo.php的脚本文件,用来调试服务是否正常

<?php
phpinfo();

由于容器的80端口是挂载到宿主机的10080端口,我们通过该端口访问服务
访问地址为:http://api.dev.com:10080/phpinfo.php

image.png

  • 配置前端nginx文件:web.conf
    在宿主机/etc/hosts文件将web.dev.com映射到127.0.0.1
    此处为了解决前端跨域问题,将/api/开头的路径转发到api.dev.com
server {
        listen       80;
        server_name  web.dev.com;
        root         /data/web/;

        location / {
            index index.php index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

        location ^~/api/ {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP        $remote_addr;
            proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy    true;
            proxy_pass http://@back/;
        }
}

upstream @back{
        server api.dev.com;
}

我们在容器/data/web/目录创建一个index.html文件,内容如下

<html>
    <body>
        nice to meet you!
    </body>
</html>

由于容器的80端口是挂载到宿主机的10080端口,我们通过该端口访问服务
访问地址为:http://web.dev.com:10080/index.html

image.png

至此,开发者本地nginx+php-fpm的开发环境已经准备完成,开发者通过拉取该镜像使用即可(开发者本地需要安装docker),本地无需安装任何环境软件。

远程开发

除上面的场景外,还可以用于以下场景

  • 我们还可以在一台开发机上为每个开发者创建一个容器,然后开发者可以通过ssh服务远程登录到容器里面,这样开发者本地docker也不需要安装

  • 在一些swoole框架,比如swoft需要读取文件注解的,如果通过挂载的方式把代码挂到容器里面,在代码变更热重启时会很慢,影响开发体验,这时可以通过ssh自带sftp服务直接将代码直接同步到容器里面,这样容器热重启会快许多

以下我们基于swoft框架,用phpstorm演示一下如何远程开发容器里面的代码:
入口为:tools > Deployment > Configuration

配置connection,此处容器的22端口挂载到宿主机的10022端口,我们通过宿主机的10022连接容器


image.png

我们添加一个Mappings,将本地的文件/Users/linjunda/it/tme/doc/doc_service同步到容器的/data/web/

image.png

tools > Deployment > Automatic upload 选择为always,该选项表示如果目录代码发生变更则自动同步到远程服务器

我们先执行一次全量的同步,入口为:tools > Deployment > Sync with deployed to


image.png

控制台显示文件同步完成信息


image.png

现在我们可以在容器里面启动这个swoft服务的热重启监听


image.png

此处容器里面doc_service服务的监听为1007,我们通过nginx配置进行端口转发

server {
        listen       80;
        server_name  doc.dev.com;
        root         /data/web/;

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP        $remote_addr;
            proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy    true;
            proxy_pass http://@docBack/;
        }
}

upstream @docBack{
        server localhost:1007;
}

我们写一个TestController

/**
 * @Controller(prefix="test")
 */
class TestController
{
    /**
     * @RequestMapping(route="test")
     */
    public function test(Response $response)
    {
        return $response->withContent("hello world");
    }
}

此处我们将doc.dev.com域名的所有请求转发到1007端口下面,通过宿主机访问如下


image.png

现在我们再IDE修改脚本文件后就会远程同步到容器里面,容器里面的swoft热重启服务监听到文件变化时会触发服务重启,这样我们就相当于远程在调试容器里面的代码了。

接下来我们在本地IDE把返回内容改为“nice to meet you!”,再访问原来的地址,内容如下:


image.png

以上就是本次的一些分享,如果转载请标明远处,觉得对你有帮助的话,请点个赞吧 _

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

推荐阅读更多精彩内容