<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>imwildcat (WildCat)</title>
    <link>https://ruby-china.org/imwildcat</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>我回来了，Ruby on Rails</title>
      <description>&lt;p&gt;&lt;a href="https://blog.wildcat.io/2024/08/i-m-back-rails-zh/" rel="nofollow" target="_blank"&gt;https://blog.wildcat.io/2024/08/i-m-back-rails-zh/&lt;/a&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sun, 04 Aug 2024 04:46:47 +0800</pubDate>
      <link>https://ruby-china.org/topics/43835</link>
      <guid>https://ruby-china.org/topics/43835</guid>
    </item>
    <item>
      <title>再见啦，Ruby on Rails</title>
      <description>&lt;p&gt;由于今天 DHH 火速 merge 了这个 PR（&lt;a href="https://github.com/hotwired/turbo/pull/971" rel="nofollow" target="_blank"&gt;https://github.com/hotwired/turbo/pull/971&lt;/a&gt;），感觉对这些年 Rails 的管理非常失望。水一篇博客：
&lt;a href="https://blog.wildcat.io/2023/09/goodbye_rails_zh/" rel="nofollow" target="_blank"&gt;https://blog.wildcat.io/2023/09/goodbye_rails_zh/&lt;/a&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Thu, 07 Sep 2023 10:45:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/43312</link>
      <guid>https://ruby-china.org/topics/43312</guid>
    </item>
    <item>
      <title>Rails 7.0 正式版发布啦！</title>
      <description>&lt;p&gt;&lt;a href="https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision" rel="nofollow" target="_blank"&gt;https://rubyonrails.org/2021/12/15/Rails-7-fulfilling-a-vision&lt;/a&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Thu, 16 Dec 2021 09:40:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/41990</link>
      <guid>https://ruby-china.org/topics/41990</guid>
    </item>
    <item>
      <title>有没有什么 Go 的 ORM 兼容 ActiveRecord 的 convention？想用 Rails 写大部分逻辑，Go 写高性能部分</title>
      <description>&lt;p&gt;似乎 gorm 的 convention 有点类似：&lt;a href="https://gorm.io/docs/models.html" rel="nofollow" target="_blank"&gt;https://gorm.io/docs/models.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Mon, 09 Aug 2021 13:18:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/41563</link>
      <guid>https://ruby-china.org/topics/41563</guid>
    </item>
    <item>
      <title>[苏州][内推] 微软：Outlook 团队招聘 Mac / Android / iOS 开发 (高分应用，HC 充足，title 不限)</title>
      <description>&lt;p&gt;Outlook（苏州）最近开放了大量职位，欢迎大家内推申请。机会难得。&lt;/p&gt;
&lt;h3 id="团队介绍"&gt;团队介绍&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Outlook Mobile 团队介绍（大王叫我来招人）：&lt;a href="https://zhuanlan.zhihu.com/p/47614033" rel="nofollow" target="_blank"&gt;https://zhuanlan.zhihu.com/p/47614033&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;程序媛的日常：&lt;a href="https://weibo.com/1691457394/H3Gy2fANF" rel="nofollow" target="_blank"&gt;https://weibo.com/1691457394/H3Gy2fANF&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;在微软工作是什么体验：&lt;a href="https://www.v2ex.com/t/499367" rel="nofollow" target="_blank"&gt;https://www.v2ex.com/t/499367&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="一般要求"&gt;一般要求&lt;/h3&gt;
&lt;p&gt;Strong coding skills, and ability to write clean code
Strong in English reading and writing with workable oral English
Strong team spirit, easy to collaborate
Strong learning ability, willing to learn and try new things out
Previous relevant experience is a big plus
我们需要英文简历（或者中英文都有），对于申请的小伙伴，我会负责跟踪进度，解答苏州相关的问题等等。另外，我们会对简历信息严格保密，请放心！&lt;/p&gt;

&lt;p&gt;联系方式：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;邮件：wildcat.io[at]outlook.com（需要公司邮箱请加我微信）&lt;/li&gt;
&lt;li&gt;微信：&lt;img src="https://l.ruby-china.com/photo/imwildcat/b08757b0-d4b3-4bc7-9f1d-03a0eba89355.png!large" width="200px" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sat, 23 Jan 2021 09:24:58 +0800</pubDate>
      <link>https://ruby-china.org/topics/40843</link>
      <guid>https://ruby-china.org/topics/40843</guid>
    </item>
    <item>
      <title>微软苏州：Outlook Mobile 团队招 Android 小伙伴啦！其他职位也可帮推</title>
      <description>&lt;p&gt;Outlook Android 团队招人啦！&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JD: &lt;a href="https://careers.microsoft.com/us/en/job/919258/Software-Development-Engineer-all-level" rel="nofollow" target="_blank"&gt;https://careers.microsoft.com/us/en/job/919258/Software-Development-Engineer-all-level&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;所有职位列表：&lt;a href="https://careers.microsoft.com/us/en/search-results?qcountry=China" rel="nofollow" target="_blank"&gt;https://careers.microsoft.com/us/en/search-results?qcountry=China&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;内推联系方式：请直接发送中英文简历到我的邮箱：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ZGFjaG9uQG1pY3Jvc29mdC5jb20K"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;欢迎咨询内推 (⁎⁍ᴗ⁍̴̛⁎)
弹性工作，全球协作环境，Google Play &amp;amp; App Store 高分应用，欢迎 Android 小伙伴加入！&lt;/p&gt;

&lt;p&gt;其他介绍：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;程序媛的一天：&lt;a href="https://m.weibo.cn/detail/4308785423716383" rel="nofollow" target="_blank"&gt;https://m.weibo.cn/detail/4308785423716383&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;在微软工作是什么体验： &lt;a href="https://www.v2ex.com/t/499367" rel="nofollow" target="_blank"&gt;https://www.v2ex.com/t/499367&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2020 新开始！微软苏州招人啦！（文末多图）：&lt;a href="https://zhuanlan.zhihu.com/p/103265468" rel="nofollow" target="_blank"&gt;https://zhuanlan.zhihu.com/p/103265468&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/a8e9d548-0f3e-4987-9185-5f7c617969e2.jpg!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Wed, 21 Oct 2020 14:28:07 +0800</pubDate>
      <link>https://ruby-china.org/topics/40501</link>
      <guid>https://ruby-china.org/topics/40501</guid>
    </item>
    <item>
      <title>微软苏州 Microsoft Teams 团队招人啦！</title>
      <description>&lt;p&gt;Microsoft Teams（&lt;a href="http://teams.microsoft.com/Start" rel="nofollow" target="_blank" title=""&gt;http://teams.microsoft.com/Start&lt;/a&gt;）招人啦！JD 链接：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://careers.microsoft.com/professionals/us/en/job/818872/Software-Engineer-all-level-Microsoft-Teams" rel="nofollow" target="_blank" title=""&gt;https://careers.microsoft.com/professionals/us/en/job/818872/Software-Engineer-all-level-Microsoft-Teams&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;内推联系方式：请直接发送中英文简历到我的邮箱：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ZGFjaG9uQG1pY3Jvc29mdC5jb20K"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简介（以 &lt;a href="https://careers.microsoft.com/professionals/us/en/job/818872/Software-Engineer-all-level-Microsoft-Teams" rel="nofollow" target="_blank" title=""&gt;JD&lt;/a&gt; 为准）：&lt;/p&gt;

&lt;p&gt;Teams 是团队协作的关键设施，已被更多的用于远程学习，交流和协作。
我们正处在创新和增长的初期，各种功能和设备等待开发，快来加入并与团队一起成长！&lt;/p&gt;

&lt;p&gt;技术栈：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;React/React Native&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Android/iOS 开发&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UWP (通用 Windows 平台)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;前端开发&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;其他介绍：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;程序媛的一天：&lt;a href="https://m.weibo.cn/detail/4308785423716383" rel="nofollow" target="_blank"&gt;https://m.weibo.cn/detail/4308785423716383&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;在微软工作是什么体验： &lt;a href="https://www.v2ex.com/t/499367" rel="nofollow" target="_blank"&gt;https://www.v2ex.com/t/499367&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2020 新开始！微软苏州招人啦！（文末多图）：&lt;a href="https://zhuanlan.zhihu.com/p/103265468" rel="nofollow" target="_blank"&gt;https://zhuanlan.zhihu.com/p/103265468&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>imwildcat</author>
      <pubDate>Mon, 13 Apr 2020 13:06:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/39734</link>
      <guid>https://ruby-china.org/topics/39734</guid>
    </item>
    <item>
      <title>[苏州][内推] 微软：Outlook Mobile 团队招聘后端 / Android / iOS / Engineering Manager (高分应用，HC 充足)</title>
      <description>&lt;p&gt;Outlook Mobile（苏州）最近开放了大量职位，欢迎大家内推申请。机会难得，可年前面试，年后入职。&lt;/p&gt;
&lt;h3 id="团队介绍"&gt;团队介绍&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Outlook Mobile 团队介绍（大王叫我来招人）：&lt;a href="https://zhuanlan.zhihu.com/p/47614033" rel="nofollow" target="_blank"&gt;https://zhuanlan.zhihu.com/p/47614033&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;程序媛的日常：&lt;a href="https://weibo.com/1691457394/H3Gy2fANF" rel="nofollow" target="_blank"&gt;https://weibo.com/1691457394/H3Gy2fANF&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;在微软工作是什么体验：&lt;a href="https://www.v2ex.com/t/499367" rel="nofollow" target="_blank"&gt;https://www.v2ex.com/t/499367&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="招聘职位"&gt;招聘职位&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Service Engineer（JD 稍后补上）: experience in Java, C++ and/or C# preferred&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://careers.microsoft.com/us/en/job/730644/Mobile-Engineer-Android-iOS-O365" rel="nofollow" target="_blank" title=""&gt;iOS Engineer&lt;/a&gt;: experience in Swift preferred (fine if you're passionate about iOS app development and ready to learn)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://careers.microsoft.com/us/en/job/730644/Mobile-Engineer-Android-iOS-O365" rel="nofollow" target="_blank" title=""&gt;Android Engineer&lt;/a&gt;: experience in Java and Kotlin preferred (fine if you're passionate about iOS app development and ready to learn)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://careers.microsoft.com/us/en/job/738284/Sr-SW-Engineering-Manager-O365-OM-team" rel="nofollow" target="_blank" title=""&gt;Engineering Manager&lt;/a&gt;: 2+ years experience in people manager role (still strong in hands on work)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="一般要求"&gt;一般要求&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Strong coding skills, and ability to write clean code&lt;/li&gt;
&lt;li&gt;Strong in English reading and writing with workable oral English&lt;/li&gt;
&lt;li&gt;Strong team spirit, easy to collaborate&lt;/li&gt;
&lt;li&gt;Strong learning ability, willing to learn and try new things out&lt;/li&gt;
&lt;li&gt;Previous relevant experience is a big plus&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们需要英文简历（或者中英文都有），对于申请的小伙伴，我会负责跟踪进度，解答苏州相关的问题等等。另外，我们会对简历信息严格保密，请放心！&lt;/p&gt;

&lt;p&gt;联系方式：dachon[at]microsoft.com&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sun, 08 Dec 2019 10:54:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/39309</link>
      <guid>https://ruby-china.org/topics/39309</guid>
    </item>
    <item>
      <title>在 Rails 项目中使用 Docker 和 GitLab CI 高效构建镜像 (第一部分)</title>
      <description>&lt;blockquote&gt;
&lt;p&gt;原文地址：&lt;a href="https://blog.wildcat.io/2019/06/rails-with-docker-part-1-zh/" rel="nofollow" target="_blank" title=""&gt;https://blog.wildcat.io/2019/06/rails-with-docker-part-1-zh/&lt;/a&gt;，英文版：&lt;a href="https://blog.wildcat.io/2019/06/rails-with-docker-part-1/" rel="nofollow" target="_blank" title=""&gt;https://blog.wildcat.io/2019/06/rails-with-docker-part-1/&lt;/a&gt;。转载请注明原文出处。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;Docker 是令人惊艳的 DevOps 工具，通过使用它可以节省大把时间，即使对个人项目来说也是如此，然而，把 Rails 项目有效地迁移到 Docker 技术栈上并不是一件容易的事情。通常我们会遇到如下问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;最终镜像体积过大。&lt;/li&gt;
&lt;li&gt;构建时间太长。安装项目依赖时会首先构建很多工具和库，这会消耗很多时间。&lt;/li&gt;
&lt;li&gt;不太容易复用 Docker 景象来进行测试。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在这片文章里，你可以看到一个逐步 Docker 化 Rails 项目的比较完整流程，基于 GitLab CI。这个教程，对于 Rails 的 DevOps 来说，可能不是百分百完美。但是我认为这可以是一个不错的开始 😊。&lt;/p&gt;
&lt;h2 id="文章太长，不想看怎么办？"&gt;文章太长，不想看怎么办？&lt;/h2&gt;
&lt;p&gt;如果你非常熟悉 Rails 和 Docker，或者你甚至尝试了多次这个技术栈的话，请直接跳过第一和第二节，或者直接前往 GitHub (&lt;a href="https://github.com/imWildCat/rails-docker-demo" rel="nofollow" target="_blank" title=""&gt;https://github.com/imWildCat/rails-docker-demo&lt;/a&gt;) 或者 GitLab 仓库 (&lt;a href="https://gitlab.com/imWildCat/docker-rails-demo" rel="nofollow" target="_blank" title=""&gt;https://gitlab.com/imWildCat/docker-rails-demo&lt;/a&gt;)。如果你对 Docker 和 Gitlab CI 有丰富的经验，直接阅读问末尾总结部分也是一个不错的选择。&lt;/p&gt;
&lt;h2 id="第 1 节: 开始使用 Rails 和 Docker"&gt;第 1 节：开始使用 Rails 和 Docker&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;请注意，Rails 6.0.0 稳定版现在还没发布 (2019-06-29)。请使用这个命令 &lt;code&gt;gem install --pre rails&lt;/code&gt; 安装 rc1 版本。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;从一个简单而且干净的项目开始总是极好的。我们可以使用下面的命令来创建一个 Rails 项目：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new &lt;span class="nt"&gt;-d&lt;/span&gt; postgresql &lt;span class="nt"&gt;--webpack&lt;/span&gt; react rails_docker_demo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个阶段，我们不需要写 Rails 代码。直接进入 Docker 部分吧。&lt;/p&gt;

&lt;p&gt;所以，&lt;code&gt;Dockerfile&lt;/code&gt; 应该是什么样子呢？&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ruby:2.6.3-alpine3.8&lt;/span&gt;

&lt;span class="c"&gt;# Install alpine packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  build-base &lt;span class="se"&gt;\
&lt;/span&gt;  busybox &lt;span class="se"&gt;\
&lt;/span&gt;  ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;  cmake &lt;span class="se"&gt;\
&lt;/span&gt;  curl &lt;span class="se"&gt;\
&lt;/span&gt;  git &lt;span class="se"&gt;\
&lt;/span&gt;  tzdata &lt;span class="se"&gt;\
&lt;/span&gt;  gnupg1 &lt;span class="se"&gt;\
&lt;/span&gt;  graphicsmagick &lt;span class="se"&gt;\
&lt;/span&gt;  libffi-dev &lt;span class="se"&gt;\
&lt;/span&gt;  libsodium-dev &lt;span class="se"&gt;\
&lt;/span&gt;  nodejs &lt;span class="se"&gt;\
&lt;/span&gt;  yarn &lt;span class="se"&gt;\
&lt;/span&gt;  openssh-client &lt;span class="se"&gt;\
&lt;/span&gt;  postgresql-dev &lt;span class="se"&gt;\
&lt;/span&gt;  tzdata

&lt;span class="c"&gt;# Define WORKDIR&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Use bunlder to avoid exit with code 1 bugs while doing integration test&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;bundler &lt;span class="nt"&gt;-v&lt;/span&gt; 2 &lt;span class="nt"&gt;--no-doc&lt;/span&gt;

&lt;span class="c"&gt;# Copy dependency manifest&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile Gemfile.lock /app/&lt;/span&gt;

&lt;span class="c"&gt;# Install Ruby dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle update &lt;span class="nt"&gt;--bundler&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--jobs&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 &lt;span class="nt"&gt;--without&lt;/span&gt; development &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /usr/local/bundle/bundler/gems/&lt;span class="k"&gt;*&lt;/span&gt;/.git /usr/local/bundle/cache/

&lt;span class="c"&gt;# Copy JavaScript dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock /app/&lt;/span&gt;

&lt;span class="c"&gt;# Install JavaScript dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Define basic environment variables&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_LOG_TO_STDOUT true&lt;/span&gt;

&lt;span class="c"&gt;# Copy source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app/&lt;/span&gt;

&lt;span class="c"&gt;# Build front-end assets&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails webpacker:verify_install
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nein bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails assets:precompile

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./bin/entrypoint.sh

&lt;span class="c"&gt;# Define entrypoint&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./bin/entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你也可以在项目仓库的 &lt;code&gt;v1-base-case&lt;/code&gt; 下（&lt;a href="https://github.com/imWildCat/rails-docker-demo/tree/v1-base-case" rel="nofollow" target="_blank" title=""&gt;GitHub&lt;/a&gt; 或者 &lt;a href="https://gitlab.com/imWildCat/docker-rails-demo/tree/v1-base-case" rel="nofollow" target="_blank" title=""&gt;GitLab&lt;/a&gt;），找到 &lt;code&gt;bin/entrypoint.sh&lt;/code&gt; 的内容。&lt;/p&gt;

&lt;p&gt;现在我们来看 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Prevent any locale errors&lt;/span&gt;
  &lt;span class="na"&gt;LC_ALL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;C.UTF-8&lt;/span&gt;
  &lt;span class="na"&gt;LANG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US.UTF-8&lt;/span&gt;
  &lt;span class="na"&gt;LANGUAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en_US.UTF-8&lt;/span&gt;

&lt;span class="na"&gt;build_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:stable&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:dind&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE:latest&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker pull $IMAGE_TAG || echo "No pre-built image found."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --cache-from $IMAGE_TAG -t $IMAGE_TAG . || docker build -t $IMAGE_TAG .&lt;/span&gt; &lt;span class="c1"&gt;# Use cache for building if possible&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $IMAGE_TAG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个 CI 配置文件使用 &lt;code&gt;--cahce-from&lt;/code&gt; 做了一些缓存，但是它没有特别大的作用。一旦我们修改了 alpine 的依赖库，整个流程都会被重新构建。在改进缓存之前，我们不如先部署这个 Rails 镜像试试。到目前位置，构建好的 Docker 镜像还不能被直接部署。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;构建时间：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails 项目：5 分 51 秒（从零开始，估计时间)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;有关这部分的详细改动，请阅读这个 Merge Request：&lt;a href="https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/1" rel="nofollow" target="_blank" title=""&gt;https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/1&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="第 1.1 节：关于部署的改进"&gt;第 1.1 节：关于部署的改进&lt;/h3&gt;
&lt;p&gt;对于部署来说，我们都需要做哪些工作呢？&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一个反向代理，比如 nginx&lt;/li&gt;
&lt;li&gt;数据库配置&lt;/li&gt;
&lt;li&gt;密钥或者&lt;code&gt;master.key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;（可选）任务队列，比如 sidekiq&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因为这个话题不是很契合这篇文章的主题，我想用一个 PR 来展示改动：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/3" rel="nofollow" target="_blank" title=""&gt;https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;部署大概步骤如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;下载并修改 &lt;code&gt;docker-compose.yml&lt;/code&gt; 到你的宿主机。&lt;/li&gt;
&lt;li&gt;（可选）把 &lt;code&gt;master.key&lt;/code&gt; 移动到宿主机的指定位置（&lt;code&gt;docker-compose.yml&lt;/code&gt; 有设定）。&lt;/li&gt;
&lt;li&gt;拉取镜像：&lt;code&gt;sudo docker-compose pull&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;启动服务：&lt;code&gt;sudo docker-compose up&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;（可选）在停止服务后，你可以执行 &lt;code&gt;sudo docker-compose down&lt;/code&gt; 删除之前创建的所有资源。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们也可以了解以下内容：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;我们也可以对 Rails 服务设置 &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; 这个环境变量。&lt;/li&gt;
&lt;li&gt;nginx 服务器的端口可以被暴露出来，或者也可以使用宿主机级别的反向代理比如 Traefik。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;构建时间：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails 项目：5:09（从零开始，估算时间），1:47（使用缓存，估算时间）&lt;/li&gt;
&lt;li&gt;反向代理：0:43（估算时间）&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;在部署之后，你会看到 404 页面。因为我们没有写任何业务逻辑，所以这没问题。&lt;/p&gt;
&lt;h2 id="第 2 节：Multi-stage 构建"&gt;第 2 节：Multi-stage 构建&lt;/h2&gt;
&lt;p&gt;在前一节构建的景象里，有一个非常明显的问题，就是镜像太大了（大概 617MB）。所以，我们可以做些什么来减小这个 Docker 镜像的体积呢？&lt;/p&gt;

&lt;p&gt;首先，让我们来看看 &lt;code&gt;Dockerfile&lt;/code&gt;。这里大概有三部分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;安装系统依赖&lt;/li&gt;
&lt;li&gt;根据 &lt;code&gt;Gemfile&lt;/code&gt; 和 &lt;code&gt;package.json&lt;/code&gt; 安装项目依赖&lt;/li&gt;
&lt;li&gt;构建前端资源，设置基础环境&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;不错的事情是，我们可以利用 Docker 的 &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="nofollow" target="_blank" title=""&gt;multi-stage builds&lt;/a&gt;。在这个教程里，我不会太深入探讨这个特性，但是它有很大的潜力，值得探索。&lt;/p&gt;

&lt;p&gt;在这个章节里，我们需要把原来的 &lt;code&gt;Dockerfile&lt;/code&gt; 分为两部分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Dockerfile.builder&lt;/code&gt;：安装依赖&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dockerfile&lt;/code&gt;：构建最终 Docker 镜像&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们应该把 &lt;code&gt;Dockerfile&lt;/code&gt; 里在最终构建 Rails 应用之前的代码移动到 &lt;code&gt;Dockerfile.builder&lt;/code&gt; 里：&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage for dependencies installation&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ruby:2.6.3-alpine3.8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="c"&gt;# Install alpine packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  build-base &lt;span class="se"&gt;\
&lt;/span&gt;  busybox &lt;span class="se"&gt;\
&lt;/span&gt;  ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;  cmake &lt;span class="se"&gt;\
&lt;/span&gt;  curl &lt;span class="se"&gt;\
&lt;/span&gt;  git &lt;span class="se"&gt;\
&lt;/span&gt;  tzdata &lt;span class="se"&gt;\
&lt;/span&gt;  gnupg1 &lt;span class="se"&gt;\
&lt;/span&gt;  graphicsmagick &lt;span class="se"&gt;\
&lt;/span&gt;  libffi-dev &lt;span class="se"&gt;\
&lt;/span&gt;  libsodium-dev &lt;span class="se"&gt;\
&lt;/span&gt;  nodejs &lt;span class="se"&gt;\
&lt;/span&gt;  yarn &lt;span class="se"&gt;\
&lt;/span&gt;  openssh-client &lt;span class="se"&gt;\
&lt;/span&gt;  postgresql-dev &lt;span class="se"&gt;\
&lt;/span&gt;  tzdata

&lt;span class="c"&gt;# Define WORKDIR&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Use bunlder to avoid exit with code 1 bugs while doing integration test&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;bundler &lt;span class="nt"&gt;-v&lt;/span&gt; 2 &lt;span class="nt"&gt;--no-doc&lt;/span&gt;

&lt;span class="c"&gt;# Copy dependency manifest&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile Gemfile.lock /app/&lt;/span&gt;

&lt;span class="c"&gt;# Install Ruby dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle update &lt;span class="nt"&gt;--bundler&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--jobs&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--retry&lt;/span&gt; 3 &lt;span class="nt"&gt;--without&lt;/span&gt; development &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Copy JavaScript dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock /app/&lt;/span&gt;

&lt;span class="c"&gt;# Install JavaScript dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们也需要更新 &lt;code&gt;Dockerfile&lt;/code&gt;，使用两个 stages，由此我们可以减小最终的镜像体积。&lt;/p&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ARG: https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; BUILDER_IMAGE_TAG&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;$BUILDER_IMAGE_TAG&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="c"&gt;# Define basic environment variables&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_LOG_TO_STDOUT true&lt;/span&gt;

&lt;span class="c"&gt;# Copy source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app/&lt;/span&gt;

&lt;span class="c"&gt;# Build front-end assets&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails webpacker:verify_install
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nein bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails assets:precompile

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ruby:2.6.3-alpine3.8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deploy&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;  curl &lt;span class="se"&gt;\
&lt;/span&gt;  tzdata &lt;span class="se"&gt;\
&lt;/span&gt;  gnupg1 &lt;span class="se"&gt;\
&lt;/span&gt;  graphicsmagick &lt;span class="se"&gt;\
&lt;/span&gt;  libsodium-dev &lt;span class="se"&gt;\
&lt;/span&gt;  nodejs &lt;span class="se"&gt;\
&lt;/span&gt;  postgresql-dev &lt;span class="se"&gt;\
&lt;/span&gt;  bash

&lt;span class="c"&gt;# Define basic environment variables&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_LOG_TO_STDOUT true&lt;/span&gt;
&lt;span class="c"&gt;# Defined for future testing&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_SERVE_STATIC_FILES true&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/local/bundle/ /usr/local/bundle/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/ /var/www/app/&lt;/span&gt;
&lt;span class="c"&gt;# We will copy the files in to /app/public while app is starting.&lt;/span&gt;
&lt;span class="c"&gt;# Otherwise, the asset files may not be updated if we use named volume.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/public /var/www/app/public_temp&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./bin/entrypoint.sh

&lt;span class="c"&gt;# Define entrypoint&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./bin/entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，我们也需要更新 CI 的配置 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="na"&gt;build_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:stable&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:dind&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG&lt;/span&gt;
    &lt;span class="na"&gt;BUILDER_IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE/builder:latest&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker pull $BUILDER_IMAGE_TAG || echo "No pre-built image found."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --cache-from $BUILDER_IMAGE_TAG -t $BUILDER_IMAGE_TAG -f Dockerfile.builder . || docker build -t $BUILDER_IMAGE_TAG -f Dockerfile.builder .&lt;/span&gt; 
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $BUILDER_IMAGE_TAG&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker pull $IMAGE_TAG || echo "No pre-built image found."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --cache-from $IMAGE_TAG --build-arg BUILDER_IMAGE_TAG=${BUILDER_IMAGE_TAG} -t $IMAGE_TAG . || docker build --build-arg BUILDER_IMAGE_TAG=${BUILDER_IMAGE_TAG} -t $IMAGE_TAG .&lt;/span&gt; &lt;span class="c1"&gt;# Use cache for building if possible&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $IMAGE_TAG&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请注意我们在这里使用了：&lt;a href="https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg" rel="nofollow" target="_blank" title=""&gt;build-time variables (--build-arg)&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;另外，在 &lt;code&gt;bin/entrypoint.sh&lt;/code&gt; 里也用了一点小技巧来处理静态资源：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; public/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="c"&gt;# Remove assets in named volume&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; public_temp/&lt;span class="k"&gt;*&lt;/span&gt; public/ &lt;span class="c"&gt;# Copy new files from new image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这些改进，我们可以把最终镜像大小大概从 617MB 减小到 &lt;strong&gt;221MB&lt;/strong&gt; (&lt;strong&gt;64.1%&lt;/strong&gt;)。时间消耗大致相同。&lt;/p&gt;

&lt;p&gt;关于细节，可以阅读这个 Merge Request: &lt;a href="https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/4" rel="nofollow" target="_blank" title=""&gt;https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/1&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="第 3 节：按需的 multi-stage 构建"&gt;第 3 节：按需的 multi-stage 构建&lt;/h2&gt;
&lt;p&gt;在引入 multi-stage 构建后，你可能想知道，我们是否可以只在有必要的情况下构建 builder。因为对于绝大多数 commits 和 PRs 来说，我们不会改动依赖。只有业务逻辑是经常变动的。我们可以利用 GitLab CI 的 &lt;code&gt;only&lt;/code&gt; 配置（&lt;a href="https://docs.gitlab.com/ee/ci/yaml/#onlyexcept-basic" rel="nofollow" target="_blank" title=""&gt;https://docs.gitlab.com/ee/ci/yaml/#onlyexcept-basic&lt;/a&gt;）和‘stage’特性。他们非常好用。&lt;/p&gt;

&lt;p&gt;首先，我们可以在 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 文件头部添加 &lt;code&gt;stages&lt;/code&gt; 设定。&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prebuild&lt;/span&gt;
  &lt;span class="c1"&gt;# - test # For future work&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="c1"&gt;# - deploy # For future work&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，builder 的构建逻辑需要被移动到一个新的 stage &lt;code&gt;prebuild&lt;/code&gt; 里：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;construct_builder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prebuild&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:stable&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:dind&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Dockerfile.builder&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Gemfile&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Gemfile.lock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;package.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn.lock&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;BUILDER_IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE/builder:latest&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker pull $BUILDER_IMAGE_TAG || echo "No pre-built image found."&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --cache-from $BUILDER_IMAGE_TAG -t $BUILDER_IMAGE_TAG -f Dockerfile.builder . || docker build -t $BUILDER_IMAGE_TAG -f Dockerfile.builder .&lt;/span&gt; 
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $BUILDER_IMAGE_TAG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请注意，为了简单，我们这里继续使用 &lt;code&gt;latest&lt;/code&gt; 标签。你可能需要根据需求改动它。&lt;/p&gt;

&lt;p&gt;所以我们也可以继续简化 &lt;code&gt;build_image&lt;/code&gt;：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker pull $BUILDER_IMAGE_TAG&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --build-arg BUILDER_IMAGE_TAG=${BUILDER_IMAGE_TAG} -t $IMAGE_TAG .&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push $IMAGE_TAG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;类似地，我们也需要在 &lt;code&gt;build_reverse_proxy&lt;/code&gt; 里也使用这个方法。它的 stage 需要被改为 &lt;code&gt;prebuild&lt;/code&gt;，以备 &lt;code&gt;construct_builder&lt;/code&gt; 构建失败。在这个特殊情况里，如果对反向代理做了任何改动，&lt;code&gt;build_reverse_proxy&lt;/code&gt; 不会被触发了。&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build_reverse_proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prebuild&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;changes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;misc/reverse_proxy/**/*&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;misc/reverse_proxy/Dockerfile&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后，我们终于可以只在必须的情况下，构建这些基础镜像了。构建 &lt;code&gt;builder&lt;/code&gt; 这个基础镜像会花很多时间。通过这些工作，我们可以剩下很多 GitLab CI 月度配额。实际上，我们自己的时间也节约了很多。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;构建时间&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Builder（只会在依赖改变时触发）：4:38（估算） &lt;/li&gt;
&lt;li&gt;Rails 应用：02:14（估算），节约了 &lt;strong&gt;50%&lt;/strong&gt; 的时间，相对于上一节。&lt;/li&gt;
&lt;li&gt;反向代理（只会在依赖改变时触发）：没有变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;详情可阅读这个 Merge Request：&lt;a href="https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/5" rel="nofollow" target="_blank" title=""&gt;https://gitlab.com/imWildCat/docker-rails-demo/merge_requests/1&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;实际上，这个教程用了如下的技术来加速构建和减少最终镜像大小：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--cahce-from&lt;/code&gt; option: &lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="nofollow" target="_blank" title=""&gt;https://docs.docker.com/engine/reference/commandline/build/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Multi-stage build: &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="nofollow" target="_blank" title=""&gt;https://docs.docker.com/develop/develop-images/multistage-build/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Build-time variables (&lt;code&gt;--build-arg&lt;/code&gt;): &lt;a href="https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg" rel="nofollow" target="_blank" title=""&gt;https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitLab CI: &lt;code&gt;only&lt;/code&gt;: &lt;a href="https://docs.gitlab.com/ee/ci/yaml/#onlyexcept-basic" rel="nofollow" target="_blank" title=""&gt;https://docs.gitlab.com/ee/ci/yaml/#onlyexcept-basic&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通过使用这些技术，我们不仅可以节约构建时间，也可以降低最终构建镜像的大小，从而获得愉悦的开发和部署体验。&lt;/p&gt;
&lt;h3 id="未来的话题"&gt;未来的话题&lt;/h3&gt;
&lt;p&gt;这里本来应该有两个或者更多关于测试 和 lint 的话题，不过我准备延后发布这些内容。因为前三节花费了我太多时间。实际上，在 Docker Swarm 上使用 &lt;a href="https://traefik.io" rel="nofollow" target="_blank" title=""&gt;Traefik&lt;/a&gt; 和 &lt;a href="https://www.portainer.io" rel="nofollow" target="_blank" title=""&gt;Portainer&lt;/a&gt; webhooks 可以完全自动化这个 DevOps 流程。我想以后的空闲时间再来写这些。&lt;/p&gt;
&lt;h3 id="小提示"&gt;小提示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果你不希望在你的开发机器上使用 Docker，&lt;a href="https://code.visualstudio.com/docs/remote/remote-overview" rel="nofollow" target="_blank" title=""&gt;VS Code Remote Development&lt;/a&gt; 是一个非常棒的工具，可以在远程机器上使用。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sat, 29 Jun 2019 16:39:37 +0800</pubDate>
      <link>https://ruby-china.org/topics/38766</link>
      <guid>https://ruby-china.org/topics/38766</guid>
    </item>
    <item>
      <title>ActiveStorage 的若干问题：1. 如何在 where 语句里判断是否 attached? 2. purge 后， attached? 为 true 但是 blob 是 nil</title>
      <description>&lt;p&gt;ActiveStorage 的若干问题：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;如何在 where 语句里判断是否 attached？&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/52418114/how-to-query-records-that-have-an-activestorage-attachment" rel="nofollow" target="_blank"&gt;https://stackoverflow.com/questions/52418114/how-to-query-records-that-have-an-activestorage-attachment&lt;/a&gt;
这有说，但是需要先把 ids 请求出来&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;purge 后，attached? 为 true 但是 blob 是 nil。&lt;br&gt;
类似这个问题：&lt;a href="https://stackoverflow.com/questions/50223308/activestorage-record-returns-attached-as-true-but-the-blob-is-nil-how-do-i-re" rel="nofollow" target="_blank"&gt;https://stackoverflow.com/questions/50223308/activestorage-record-returns-attached-as-true-but-the-blob-is-nil-how-do-i-re&lt;/a&gt;
是不是可以去提 issues 了呢？&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;现在项目上线了但是非常后悔用 Active Storage。感觉实在是太不好用了&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Mon, 22 Oct 2018 18:26:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/37661</link>
      <guid>https://ruby-china.org/topics/37661</guid>
    </item>
    <item>
      <title>GoRails 的 tools &amp; gems 似乎不错啊，看起来是个有意思的 RubyToolBox 的补充？</title>
      <description>&lt;p&gt;&lt;a href="https://gorails.com/tool_categories" rel="nofollow" target="_blank"&gt;https://gorails.com/tool_categories&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;对比：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ruby-toolbox.com" rel="nofollow" target="_blank"&gt;https://www.ruby-toolbox.com&lt;/a&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sun, 29 Jul 2018 04:04:22 +0800</pubDate>
      <link>https://ruby-china.org/topics/37247</link>
      <guid>https://ruby-china.org/topics/37247</guid>
    </item>
    <item>
      <title>ActiveRecord 子查询 (或者是 join) 与含有子查询的主查询缓存问题</title>
      <description>&lt;p&gt;举个例子，项目有三个 models：&lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Post&lt;/code&gt;, &lt;code&gt;UserLike&lt;/code&gt;，每次请求一个 &lt;code&gt;PostsController#show&lt;/code&gt; 时，我想顺便用一条查询获取这个用户是否为这个 &lt;code&gt;Post&lt;/code&gt; 点赞了。
如果是 raw SQL 大概是 &lt;code&gt;SELECT * FROM posts WHERE posts.id = ? LEFT JOIN user_likes ON posts.id = user_likes.id LEFT JOIN users ON posts.user_id = users.id;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ActiveRecord 里有没有办法去缓存 &lt;code&gt;SELECT * FROM posts WHERE posts.id = ?&lt;/code&gt; 这一部分，每次只查询后面的 join 部分？&lt;/p&gt;

&lt;p&gt;ER diagram 如下（可能不够标准）：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/c832306f-145b-4e2a-b196-7354191bd750.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Wed, 14 Mar 2018 22:10:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/35241</link>
      <guid>https://ruby-china.org/topics/35241</guid>
    </item>
    <item>
      <title>商科背景， 5 周完成 deeplearning.ai 课程的过程、心得与总结</title>
      <description>&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/ba251eef-57b8-4ed7-aa1b-c9d9554de9d1.jpg!large" title="" alt=""&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;deeplearning.ai 的课程已经发布一段时间了，最后一部分 Sequence Model 也在不到一周前发布。虽然最近开学课业紧张，我终于在前天赶完了课程。在此之中有一些感受，想简单写写，供大家参考。原文地址：&lt;a href="https://blog.wildcat.io/2018/02/summary-of-my-learning-experience-about-deeplearning-ai-zh/" rel="nofollow" target="_blank" title=""&gt;https://blog.wildcat.io/2018/02/summary-of-my-learning-experience-about-deeplearning-ai-zh/&lt;/a&gt; ，转载请注明出处。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="完成课程的时间与物质成本"&gt;完成课程的时间与物质成本&lt;/h2&gt;
&lt;p&gt;吴恩达（Andrew Ng）教授对中国的深度学习学习者来说真的很良心，直接把课程视频和讲义发布到 &lt;a href="http://mooc.study.163.com/smartSpec/detail/1001319001.htm" rel="nofollow" target="_blank" title=""&gt;网易云课堂&lt;/a&gt; 免费供大家观看阅读。在夏天，deeplearning.ai 课程刚刚发布的时候，我也是在网易云课堂慢慢观看课程，效率较低。而且这个课程如果抛开编程练习，意义就要小很多了。因此，在圣诞假期的末期时候，我决定去购买了 Coursera 的订阅，并在一月中旬连续刷完前三门课程，一月下旬和二月前几天完成了 Convolutional Neural Networks 和 Sequence Model 部分。时间表如下：&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;课程&lt;/th&gt;
&lt;th&gt;完成时间&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neural Networks and Deep Learning&lt;/td&gt;
&lt;td&gt;4 January, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization&lt;/td&gt;
&lt;td&gt;7 January, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structuring Machine Learning Projects&lt;/td&gt;
&lt;td&gt;9 January, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convolutional Neural Networks&lt;/td&gt;
&lt;td&gt;25 January, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sequence Models&lt;/td&gt;
&lt;td&gt;6 February, 2018&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;可以看到，整套课程完成大概用了 &lt;strong&gt;5 周&lt;/strong&gt;的时间（这个时间并不是全职完成课程，而是完成笔者大学的课程和作业的闲暇时间）。Coursera 有一个星期的试用期，最终应该只会产生一次订阅月费（&lt;strong&gt;£36&lt;/strong&gt;）。不过，实际上，对于自己来说，达到这个速度是有一定的先决条件的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;已完成 Coursera 上 Machine Learning 课程&lt;/li&gt;
&lt;li&gt;有选修现在学校的 Machine Learning 与 Introduction to Neural Computation 课程&lt;/li&gt;
&lt;li&gt;有一定的微分和线性代数基础（本科学了一部分金融和经济学，一部分 marketing，现在硕士计算机科学）&lt;/li&gt;
&lt;li&gt;全部视频都是 1.25 倍速播放（不过算上记笔记时暂停用的时间，估算速度也就大概 1.15 倍）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此，每个人情况不同，个人觉得不用特别追求速度。对于 Andrew Ng 的课程来讲，花上三个月的订阅费去学习也是性价比很高的（相比于其他同类课程的价格，这个课程已经不能再良心了）。&lt;/p&gt;
&lt;h2 id="对课程的整体感受"&gt;对课程的整体感受&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;刚入门时比较基础，从最基础的 logistic regression 讲起，学习曲线并不陡峭。另外，这门课也是比较有深度的，挖掘了 Adam、Momentum、LSTM、GRU 等复杂概念。&lt;/li&gt;
&lt;li&gt;Ng 很喜欢鼓励大家，每当讲到很复杂的概念的时候，他总是安慰大家，不用太害怕这些复杂的东西，慢慢理解就好。他总是说，自己最初也是边用边慢慢深入理解的。很喜欢 Ng 常用的一个词“intuition”，个人对这个词的理解就是，对于复杂的概念，不求甚解，在以后的使用中慢慢体会。&lt;/li&gt;
&lt;li&gt;持续时间不算长，不拖沓。不同知识点被合理分配成不同的短视频，适合零碎时间看。&lt;/li&gt;
&lt;li&gt;个人偏好 Ng 的讲义形式：手写标注和打印公式相结合。&lt;/li&gt;
&lt;li&gt;很多部分的设计，比如 notations（上标下标等），非常用心，可以看出 Ng 下了不少功夫。&lt;/li&gt;
&lt;li&gt;编程作业设计得比较好，很注意训练模型与使用 pretrained weights 相结合，节约训练时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="事后总结的，完成课程的一些小技巧"&gt;事后总结的，完成课程的一些小技巧&lt;/h2&gt;
&lt;p&gt;这部分适合刚开始学习这门课程的同学参考。&lt;/p&gt;
&lt;h3 id="Lecture 部分"&gt;Lecture 部分&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;一定一定一定&lt;/strong&gt;要做笔记，个人建议最好纸笔手写笔记，只有自己写下公式和网络架构图，写代码时才更加自信并且不容易出错。&lt;/li&gt;
&lt;li&gt;尽量多复习回顾，可能回顾一两遍后，印象很深，方便后面答 quiz 和编程作业。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="Assignment 部分"&gt;Assignment 部分&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;多用 Coursera 论坛搜索 Jupyter Notebook 的报错，也可以用 Google 搜一些比较 general 的报错，例如 Keras 和 TensorFlow 的报错。

&lt;ul&gt;
&lt;li&gt;对于 dimension 的报错，应该去想这个 dimension 是为什么错，正确的 dimension 应该是从哪里来的。&lt;/li&gt;
&lt;li&gt;一些报错很难 debug，一定要有耐心。有时候实在解决不了编程作业就等一两天，说不定过段时间就有思路了。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;一定不要去 GitHub 找代码（照抄代码骗人骗己浪费钱，多搜索锻炼思考与解决问题能力）。&lt;/li&gt;
&lt;li&gt;Jupyter Notebook 中的内容，尤其是练习后总结的蓝字要认真读，甚至做笔记。这样有助于加深对于 lectures 的理解（比如不同方法的优劣等等）。&lt;/li&gt;
&lt;li&gt;如果 Jupyter Notebook 总是输出与期望数据不相关的值，请不要总是以为习题设计有问题（早期问题大部分可能都被修复了），多从自己找原因，多搜索。如果实在被卡住，可以提问后进行下周课程以节约时间。&lt;/li&gt;
&lt;li&gt;对于 Keras 要大胆尝试，多查文档。一个很重要的点就是理解 Keras 的函数式编程 API。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="其他感受"&gt;其他感受&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;最后一个编程作业呼应了 Ng 的 Machine Learning 中讲到声音合成的部分，很有趣。&lt;/li&gt;
&lt;li&gt;预处理数据可能比根据论文复用成熟深度学习模型本身要复杂很多。&lt;/li&gt;
&lt;li&gt;虽然第三部分个人得分最低而且没有编程作业，但是依旧很值得学习，传授的是 Ng 多年总结的经验。&lt;/li&gt;
&lt;li&gt;Ng 对于一些深度学习领域的领军人物的采访，也非常值得一看。比如，GAN 的”发明人“Ian Goodfellow 入门之路竟然是从上 Andrew Ng 在斯坦福的 AI 课开始的（并且 Ng 一开始并不知道这件事）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;毋庸置疑，deeplearning.ai 这一组深度学习课程，质量是非常高的。对于入门者来说，完成这一系列课程需要非常大的毅力与耐心。完成这门课，仅仅意味着自己在这个领域刚刚入门，还有很多需要慢慢探索，比如强化学习、GAN 等。不管怎样，希望在学习深度学习路上的各位，可以早日做出自己喜欢的有趣应用。&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sun, 11 Feb 2018 19:30:08 +0800</pubDate>
      <link>https://ruby-china.org/topics/35031</link>
      <guid>https://ruby-china.org/topics/35031</guid>
    </item>
    <item>
      <title>RubyMine 的 object literal (不知道是否可以这么称呼) 的 formatting 出错</title>
      <description>&lt;p&gt;JetBrains 官方论坛发帖没人回复：&lt;a href="https://intellij-support.jetbrains.com/hc/en-us/community/posts/360000042710-Buggy-formatting-for-object-literal-Ruby-" rel="nofollow" target="_blank"&gt;https://intellij-support.jetbrains.com/hc/en-us/community/posts/360000042710-Buggy-formatting-for-object-literal-Ruby-&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;例 1:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/fb332b2b-cf5b-4390-9f26-b2dedaa6303a.png!large" title="" alt=""&gt;  &lt;/p&gt;

&lt;p&gt;会变成 &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/4997565f-6ada-429e-b7a8-2c81241818df.png!large" title="" alt=""&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;例 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/63d4dc98-7aa1-4c6e-ae35-30a0b0ab582f.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;会变成 &lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2018/0e6b5e19-4a22-4f33-a3c4-f5ad8433505f.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Sun, 11 Feb 2018 19:16:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/35030</link>
      <guid>https://ruby-china.org/topics/35030</guid>
    </item>
    <item>
      <title>Rails 5.1.4 中的 manifest.js 的作用是什么？看起来似乎是 Sprockets 4.x 的特性？</title>
      <description>&lt;p&gt;Rails 5.1.4 似乎没有 Sprockets 4.x，文档似乎也没找到很详细的解释，另外似乎也没有了 application.js 文件了？&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Fri, 29 Dec 2017 04:40:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/34789</link>
      <guid>https://ruby-china.org/topics/34789</guid>
    </item>
    <item>
      <title>有没有 Asset Pipeline 的增强 gem 支持编译 ES6 的？</title>
      <description>&lt;p&gt;想在 js 里用 ES6 特性但是感觉不想用 Webpacker（要安装的依赖太多），有没有好办法？&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Wed, 27 Dec 2017 23:18:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/34783</link>
      <guid>https://ruby-china.org/topics/34783</guid>
    </item>
    <item>
      <title>Validation 可不可以仅仅对一个 controller 的 action 生效或某个 form 生效？</title>
      <description>&lt;p&gt;想在 model 上加针对某个字段的 validation，但是不想全局生效，有没有好办法？&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Mon, 03 Jul 2017 01:10:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/33388</link>
      <guid>https://ruby-china.org/topics/33388</guid>
    </item>
    <item>
      <title>最近深感需要学习 Rails 测试了，RSpec 和自带测试框架哪个好一些？5.1 新增的系统测试有什么好的入门指南吗？(微信应用)</title>
      <description>&lt;p&gt;最近在帮朋友做一个超级复杂的系统，没有写测试。
由于逻辑复杂而且自己编程习惯不够好，感觉代码量一大就很难高效开发了——经常牵一发而动全身，改动一处导致其他地方的 bug。&lt;/p&gt;

&lt;p&gt;之前自学过一部分 Rails 测试，关于 model 的测试应该不难。但是如果接入微信，如何 mock 微信登录部分呢？还是直接跳过这部分，直接算用户登录后的情形？&lt;/p&gt;

&lt;p&gt;另外关于 &lt;code&gt;sidekiq&lt;/code&gt; 的 worker 部分如何测试比较好？&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Thu, 22 Jun 2017 20:51:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/33286</link>
      <guid>https://ruby-china.org/topics/33286</guid>
    </item>
    <item>
      <title>Rails.cache.fetch 的外层方法不能有参数么？我有参数的外层方法内部的缓存都没有执行</title>
      <description>&lt;p&gt;以钉钉 API 调用举个例子（js ticket 需要缓存）：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_js_ticket_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s1"&gt;'fetching dingding js ticket'&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://oapi.dingtalk.com/get_jsapi_ticket?access_token=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'errcode'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ticket'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s1"&gt;'Cannot get access token for Dingding'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_cached_js_ticket&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dingding_js_t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;ak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_access_token&lt;/span&gt;
    &lt;span class="n"&gt;get_js_ticket_1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里&lt;code&gt;get_cached_js_ticket&lt;/code&gt; 是&lt;strong&gt;没有参数&lt;/strong&gt;的，可以正确执行缓存。&lt;/p&gt;

&lt;p&gt;但是：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_js_ticket_2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dingding_js_t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;expires_in: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s1"&gt;'fetching dingding js ticket'&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://oapi.dingtalk.com/get_jsapi_ticket?access_token=&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'errcode'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ticket'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s1"&gt;'Cannot get access token for Dingding'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;则不能成功缓存。&lt;/p&gt;

&lt;p&gt;感觉好黑科技啊，内层的方法如何检测外层方法有没有参数？怎么实现的？&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Thu, 22 Jun 2017 00:11:53 +0800</pubDate>
      <link>https://ruby-china.org/topics/33278</link>
      <guid>https://ruby-china.org/topics/33278</guid>
    </item>
    <item>
      <title>Turbolinks 会导致微信网页在 iOS 上点击链接时出现 invalid signature 错误，Android 和开发者工具则正常</title>
      <description>&lt;p&gt;最近在做微信网页开发，用的是 &lt;code&gt;wechat&lt;/code&gt; 这个 gem。
iOS 上，目前生成的网页，首页没问题，一旦点击一次链接就会出现 invalid signature 错误。&lt;/p&gt;

&lt;p&gt;搜索了很多相关关键词，甚至关于 React 的微信网页开发的。都没找到什么可以利用的资料。
很想用 &lt;code&gt;turbolinks&lt;/code&gt;，但是现在看来只能关闭了。奇怪的是为什么 Android 正常 iOS 不正常，按理说 iOS 的浏览器应该更稳定才对。&lt;/p&gt;</description>
      <author>imwildcat</author>
      <pubDate>Tue, 20 Jun 2017 09:02:12 +0800</pubDate>
      <link>https://ruby-china.org/topics/33264</link>
      <guid>https://ruby-china.org/topics/33264</guid>
    </item>
  </channel>
</rss>
