在Docker容器中安装旧版memcached gem及Rails旧项目迁移配置问题
我之前帮朋友处理过几乎一模一样的旧Rails应用迁移需求,踩了不少坑,给你一套能直接落地的配置方案,既解决gem重复安装的问题,也搞定旧memcached gem的安装难题。
一、核心思路:用主机目录挂载保存Gems
要避免每次启动容器都重新跑bundle install,关键是把容器里的gem存储目录直接映射到主机本地。Ruby 1.9.3默认的bundle存储路径是/usr/local/bundle,我们把这个路径挂载到主机的./.bundle/gems目录,这样第一次安装的gems就会存在主机上,后续启动容器直接复用。
1. Dockerfile 配置
选ruby:1.9.3-slim作为基础镜像最合适——它基于Debian Wheezy,系统库版本比较旧,刚好适配旧Ruby和Rails的依赖需求。还要提前装编译gems需要的工具和memcached的系统依赖:
FROM ruby:1.9.3-slim # 安装编译依赖和memcached服务 RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ libmemcached-dev \ memcached \ && rm -rf /var/lib/apt/lists/* # 设置bundle的存储路径,确保挂载的目录能被正确识别 ENV BUNDLE_PATH /usr/local/bundle ENV BUNDLE_DISABLE_SHARED_GEMS 1 # 设定工作目录 WORKDIR /app # 先复制Gemfile和Gemfile.lock,利用Docker缓存优化构建速度 COPY Gemfile Gemfile.lock ./
2. docker-compose.yml 配置
重点是挂载两个目录:应用代码和gem存储目录,同时可以顺便加个memcached服务容器,方便本地开发测试:
version: '3' services: web: build: . volumes: # 把主机本地的应用代码挂载到容器工作目录 - ./:/app # 把主机的gem存储目录映射到容器的bundle路径 - ./.bundle/gems:/usr/local/bundle command: bash -c "bundle install && rails server -b 0.0.0.0" ports: - "3000:3000" # 搭配一个兼容旧gem的memcached容器 memcached: image: memcached:1.4 # 1.4系列和旧版memcached gem兼容性最好 ports: - "11211:11211"
第一次启动容器时,bundle install会把所有gems安装到主机的./.bundle/gems里,之后再启动就直接用这些已安装的gems,完全不用重复安装了。
二、搞定旧版本memcached gem的安装问题
旧版memcached gem(比如适配Rails2.3的1.8.0版本)在新环境下编译很容易出问题,我总结了几个解决办法:
1. 先锁定兼容的gem版本
确保你的Gemfile里指定的memcached版本是和Ruby 1.9.3、Rails2.3匹配的,比如:
gem 'memcached', '~> 1.8.0'
2. 编译时指定系统库路径
如果安装时出现“找不到libmemcached”的错误,在执行bundle install时加上编译参数:
bundle install --with-memcached-dir=/usr/lib/x86_64-linux-gnu
嫌每次输麻烦的话,可以在Dockerfile里加个环境变量,让bundle自动读取:
ENV MEMCACHED_DIR /usr/lib/x86_64-linux-gnu
3. 换用社区维护的fork版本
如果官方的memcached gem还是编译失败,试试memcached-northscale这个fork,它专门修复了旧Ruby环境下的编译问题,在Gemfile里替换成:
gem 'memcached-northscale', '~> 1.8.0', :require => 'memcached'
三、几个避坑提醒
- 权限问题:第一次安装后,主机的
.bundle/gems目录可能是root权限,导致后续主机操作麻烦。可以在Dockerfile里创建一个普通用户,或者在docker-compose.yml里加上user: "${UID}:${GID}"(启动时会自动用主机当前用户的ID运行容器)。 - Docker缓存优化:一定要先复制Gemfile和Gemfile.lock到容器,再执行bundle install——这样只有当Gemfile内容变化时,Docker才会重新执行bundle步骤,大大加快构建速度。
- 别用新基础镜像:千万不要用
ruby:1.9.3的非slim版或者基于新Debian/Ubuntu的镜像,新系统库和旧gem的编译逻辑不兼容,会出现一堆莫名其妙的错误。
内容的提问来源于stack exchange,提问作者Boško Ivanišević

