-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeed.xml
More file actions
1782 lines (1318 loc) · 206 KB
/
feed.xml
File metadata and controls
1782 lines (1318 loc) · 206 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>韦初一的小窝</title>
<description>精于心 简于形</description>
<link>https://archfish.top/</link>
<atom:link href="https://archfish.top/feed.xml" rel="self" type="application/rss+xml" />
<pubDate>Sun, 14 Apr 2019 00:08:35 +0800</pubDate>
<lastBuildDate>Sun, 14 Apr 2019 00:08:35 +0800</lastBuildDate>
<generator>Jekyll v3.8.3</generator>
<item>
<title>Rails use postgresql-adapter to compute postgis</title>
<description><p>rails中每种数据库都有自己的adapter,这些adapter负责处理数据库的各种数据类型,但是设计上无法自行增加新数据类型。使用postgis功能通常使用的是<a href="https://github.com/rgeo/activerecord-postgis-adapter" title="ActiveRecord connection adapter for PostGIS, based on postgresql and rgeo">active-record-postgis-adapter</a>,由于Reocar使用的功能仅仅是计算坐标点间的距离,增加这样一层适配器会导致应用结构变得复杂,同时也可能会引入<a href="https://github.com/rgeo/activerecord-postgis-adapter/issues/296" title="version 2.2.2 prepared_statements setting not take effect">新的问题</a>。在实现现有功能的情况下尝试移除active-record-postgis-adapter。</p>
<h2 id="最小需求">最小需求</h2>
<ul>
<li>计算两点之间的距离</li>
<li>找出以某点P为圆心X为半径所包含的所有点</li>
<li>找出离P点最近的点</li>
</ul>
<h2 id="思路">思路</h2>
<ul>
<li>使用PG的条件索引对longitude和latitude进行索引</li>
<li>通过裸SQL进行数据操作</li>
</ul>
<h2 id="代码">代码</h2>
<p>为了在项目中跟进数据结构变化,需要将schema dump改为structure dump</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/environments/[ENV].rb</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span>
<span class="n">config</span><span class="p">.</span><span class="nf">active_record</span><span class="p">.</span><span class="nf">schema_format</span> <span class="o">=</span> <span class="ss">:sql</span>
<span class="k">end</span>
</code></pre></div></div>
<p>rails4.1与新版本pg导出SQL会出现<a href="https://stackoverflow.com/questions/35999906/pg-dump-invalid-option-i-when-migrating" title="“pg_dump: invalid option — i” when migrating">参数异常</a>,重写该task</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/tasks/database.rake</span>
<span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="p">[</span><span class="s2">"db:structure:dump"</span><span class="p">].</span><span class="nf">clear</span>
<span class="n">namespace</span> <span class="ss">:db</span> <span class="k">do</span>
<span class="n">namespace</span> <span class="ss">:structure</span> <span class="k">do</span>
<span class="n">desc</span> <span class="s2">"Overriding the task db:structure:dump task to remove -i option from pg_dump to make postgres 9.5 compatible"</span>
<span class="n">task</span> <span class="ss">dump: </span><span class="p">[</span><span class="ss">:environment</span><span class="p">,</span> <span class="ss">:load_config</span><span class="p">]</span> <span class="k">do</span>
<span class="n">config</span> <span class="o">=</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">configurations</span><span class="p">[</span><span class="no">Rails</span><span class="p">.</span><span class="nf">env</span><span class="p">]</span>
<span class="n">set_psql_env</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
<span class="n">filename</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">,</span> <span class="s2">"db"</span><span class="p">,</span> <span class="s2">"structure.sql"</span><span class="p">)</span>
<span class="n">database</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s2">"database"</span><span class="p">]</span>
<span class="n">command</span> <span class="o">=</span> <span class="s2">"pg_dump -s -x -O -f </span><span class="si">#{</span><span class="no">Shellwords</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="no">Shellwords</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">database</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span>
<span class="k">raise</span> <span class="s1">'Error dumping database'</span> <span class="k">unless</span> <span class="no">Kernel</span><span class="p">.</span><span class="nf">system</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
<span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span> <span class="o">&lt;&lt;</span> <span class="s2">"SET search_path TO </span><span class="si">#{</span><span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">schema_search_path</span><span class="si">}</span><span class="s2">;</span><span class="se">\n\n</span><span class="s2">"</span> <span class="p">}</span>
<span class="k">if</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">supports_migrations?</span>
<span class="no">File</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
<span class="n">f</span><span class="p">.</span><span class="nf">puts</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span><span class="p">.</span><span class="nf">connection</span><span class="p">.</span><span class="nf">dump_schema_information</span>
<span class="n">f</span><span class="p">.</span><span class="nf">print</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Rake</span><span class="o">::</span><span class="no">Task</span><span class="p">[</span><span class="s2">"db:structure:dump"</span><span class="p">].</span><span class="nf">reenable</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">set_psql_env</span><span class="p">(</span><span class="n">configuration</span><span class="p">)</span>
<span class="no">ENV</span><span class="p">[</span><span class="s1">'PGHOST'</span><span class="p">]</span> <span class="o">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'host'</span><span class="p">]</span> <span class="k">if</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'host'</span><span class="p">]</span>
<span class="no">ENV</span><span class="p">[</span><span class="s1">'PGPORT'</span><span class="p">]</span> <span class="o">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'port'</span><span class="p">].</span><span class="nf">to_s</span> <span class="k">if</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'port'</span><span class="p">]</span>
<span class="no">ENV</span><span class="p">[</span><span class="s1">'PGPASSWORD'</span><span class="p">]</span> <span class="o">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'password'</span><span class="p">].</span><span class="nf">to_s</span> <span class="k">if</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'password'</span><span class="p">]</span>
<span class="no">ENV</span><span class="p">[</span><span class="s1">'PGUSER'</span><span class="p">]</span> <span class="o">=</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'username'</span><span class="p">].</span><span class="nf">to_s</span> <span class="k">if</span> <span class="n">configuration</span><span class="p">[</span><span class="s1">'username'</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>创建包含经纬度的表</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># db/migrate/201902260xxxxx_create_stores.rb</span>
<span class="k">class</span> <span class="nc">CreateStores</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
<span class="k">def</span> <span class="nf">change</span>
<span class="n">create_table</span> <span class="ss">:stores</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="n">t</span><span class="p">.</span><span class="nf">decimal</span> <span class="ss">:latitude</span><span class="p">,</span> <span class="ss">precision: </span><span class="mi">9</span><span class="p">,</span> <span class="ss">scale: </span><span class="mi">6</span>
<span class="n">t</span><span class="p">.</span><span class="nf">decimal</span> <span class="ss">:longitude</span><span class="p">,</span> <span class="ss">precision: </span><span class="mi">9</span><span class="p">,</span> <span class="ss">scale: </span><span class="mi">6</span>
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:name</span>
<span class="n">t</span><span class="p">.</span><span class="nf">timestamps</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>启用数据库插件需要特权,应用账号可能没有相关权限,请使用特权账号操作</p>
<pre><code class="language-psql">\c shop_db
create extension postgis;
</code></pre>
<p>封装一些方法</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/models/store.rb</span>
<span class="k">class</span> <span class="nc">Store</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mk_location</span>
<span class="sx">%{
ST_GeographyFromText(
'SRID=4326;POINT(' || #{table_name.inspect}.longitude || ' ' || #{table_name.inspect}.latitude || ')'
)
}</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">mk_point</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">)</span>
<span class="sx">%{ST_GeographyFromText('SRID=4326;POINT(%f %f)')}</span> <span class="sx">% [latitude, </span><span class="n">longitude</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>在经纬度上创建条件索引</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># db/migrate/20190227xxxxx_add_index_for_location.rb</span>
<span class="k">class</span> <span class="nc">AddIndexForLocation</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
<span class="k">def</span> <span class="nf">change</span>
<span class="n">execute</span> <span class="sx">%{
create index index_on_stores_location ON stores using gist (
#{Store.mk_location}
)
}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>提供相关scope</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/models/store.rb</span>
<span class="k">class</span> <span class="nc">Store</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">scope</span> <span class="ss">:nearby</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">distance_in_meters</span> <span class="o">=</span> <span class="mi">2000</span><span class="p">)</span> <span class="p">{</span>
<span class="n">where</span><span class="p">(</span><span class="sx">%{
ST_DWithin(
#{mk_location},
#{mk_point(latitude, longitude)},
%d
)
}</span> <span class="o">%</span> <span class="p">[</span><span class="n">distance_in_meters</span><span class="p">])</span>
<span class="p">}</span>
<span class="n">scope</span> <span class="ss">:nearby_ordered</span><span class="p">,</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">distance_in_meters</span> <span class="o">=</span> <span class="mi">2000</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">select</span><span class="p">(</span><span class="sx">%{
#{table_name.inspect}.*, ST_Distance(
#{mk_location}, #{mk_point(latitude, longitude)}
) as distance
}</span>
<span class="p">).</span><span class="nf">nearby</span><span class="p">(</span><span class="n">latitude</span><span class="p">,</span> <span class="n">longitude</span><span class="p">,</span> <span class="n">distance_in_meters</span><span class="p">).</span><span class="nf">reorder</span><span class="p">(</span><span class="s2">"distance asc"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1"># other code</span>
<span class="k">end</span>
</code></pre></div></div>
<h2 id="总结">总结</h2>
<ul>
<li>我个人来说不喜欢滥用gem,如果因为一个小功能引入了一个超复杂的gem,在后期维护中会带来各种各样的麻烦;</li>
<li>我大致测试了一下在POINT上创建索引和直接使用条件索引,性能差异不算太大,对于现有场景来说性能差异在可接受范围内;</li>
</ul>
<h2 id="参考">参考</h2>
<p><a href="http://ngauthier.com/2013/08/postgis-and-rails-a-simple-approach.html" title="PostGIS and Rails: A Simple Approach">PostGIS and Rails: A Simple Approach</a></p>
<hr />
<p>欢迎跟我交流 <a href="https://github.com/archfish/archfish" title="archfish blog">Archfish</a></p>
</description>
<pubDate>Sun, 14 Apr 2019 00:01:01 +0800</pubDate>
<link>https://archfish.top/2019/04/14/rails_postgresql_adapter_with_postgis/</link>
<guid isPermaLink="true">https://archfish.top/2019/04/14/rails_postgresql_adapter_with_postgis/</guid>
<category>rails</category>
<category>PostgreSQL</category>
<category>postgis</category>
</item>
<item>
<title>macOS使用brew安装用户级ruby</title>
<description><p>系统自带了一个ruby,但是这个ruby是保存在系统库中的,如果需要安装gem则会提示无权限<code class="highlighter-rouge">You don't have write permissions for the /Library/Ruby/Gems/2.3 directory.</code>。那么rvm呢,这货在安装ruby-2.3版本的时候总会出现openssl问题。</p>
<p>所以使用brew来安装一个user级的ruby,并通过简单脚本实现ruby版本切换:</p>
<h2 id="安装ruby23">安装ruby2.3</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>ruby@2.3
</code></pre></div></div>
<h2 id="更新rubygem">更新rubygem</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem update <span class="nt">--system</span>
</code></pre></div></div>
<h2 id="配置环境变量">配置环境变量</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">RUBY_HOME</span><span class="o">=</span>/usr/local/opt/ruby@2.3
<span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="nv">$RUBY_HOME</span>/lib/ruby/gems/2.3.0
<span class="nb">export </span><span class="nv">GEM_PATH</span><span class="o">=</span><span class="nv">$RUBY_HOME</span>/lib/ruby/gems/2.3.0
<span class="c"># 记录没有ruby时的环境变量</span>
<span class="nb">export </span><span class="nv">ORIG_PATH</span><span class="o">=</span><span class="nv">$PATH</span>
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$RUBY_HOME</span><span class="s2">/bin:/usr/local/lib/ruby/gems/2.3.0/bin:</span><span class="nv">$ORIG_PATH</span><span class="s2">"</span>
</code></pre></div></div>
<h2 id="封装并提供切换功能">封装并提供切换功能</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">ORIG_PATH</span><span class="o">=</span><span class="nv">$PATH</span>
<span class="k">function </span>ruby_use
<span class="o">{</span>
<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">=</span> <span class="s1">''</span> <span class="o">]</span>
<span class="k">then
</span><span class="nb">export </span><span class="nv">RUBY_MAIN_VERSION</span><span class="o">=</span><span class="s1">'2.3'</span>
<span class="k">else
</span><span class="nb">export </span><span class="nv">RUBY_MAIN_VERSION</span><span class="o">=</span><span class="nv">$1</span>
<span class="k">fi
</span><span class="nb">export </span><span class="nv">RUBY_HOME</span><span class="o">=</span><span class="s2">"/usr/local/opt/ruby@</span><span class="k">${</span><span class="nv">RUBY_MAIN_VERSION</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">RUBY_HOME</span><span class="k">}</span><span class="s2">/lib/ruby/gems/</span><span class="k">${</span><span class="nv">RUBY_MAIN_VERSION</span><span class="k">}</span><span class="s2">.0"</span>
<span class="nb">export </span><span class="nv">GEM_PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">RUBY_HOME</span><span class="k">}</span><span class="s2">/lib/ruby/gems/</span><span class="k">${</span><span class="nv">RUBY_MAIN_VERSION</span><span class="k">}</span><span class="s2">.0"</span>
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">RUBY_HOME</span><span class="k">}</span><span class="s2">/bin:/usr/local/lib/ruby/gems/</span><span class="k">${</span><span class="nv">RUBY_MAIN_VERSION</span><span class="k">}</span><span class="s2">.0/bin:</span><span class="nv">$ORIG_PATH</span><span class="s2">"</span>
<span class="o">}</span>
ruby_use
</code></pre></div></div>
<p>在需要切换ruby版本时在相应终端使用<code class="highlighter-rouge">ruby_use 版本</code>即可.</p>
<h2 id="已知问题">已知问题</h2>
<ul>
<li>不会自动在命令前加<code class="highlighter-rouge">bundle exec</code>, 解决办法:创建相关命令的alias。不推荐这样做<code class="highlighter-rouge">alias rails='bundle exec rails'</code>,会导致<code class="highlighter-rouge">rails new</code>时报找不到Gemfile错误。</li>
<li>暂不支持小版本控制</li>
<li>每次切换ruby版本只会对当前终端有效(或许这是feature…</li>
</ul>
<hr />
<p>欢迎跟我交流 <a href="https://github.com/archfish/archfish" title="archfish blog">Archfish</a></p>
</description>
<pubDate>Sat, 13 Apr 2019 23:43:08 +0800</pubDate>
<link>https://archfish.top/2019/04/13/macos_brew_manage_multiple_ruby_versions/</link>
<guid isPermaLink="true">https://archfish.top/2019/04/13/macos_brew_manage_multiple_ruby_versions/</guid>
<category>macOS</category>
<category>brew</category>
<category>ruby</category>
</item>
<item>
<title>Ruby on Rails容器化实践(二点一)</title>
<description><p>这是<a href="https://archfish.github.io/2018/08/20/ruby-on-rails-containerization-02/">Ruby on Rails容器化实践(二) - Archfish | Blog</a>的简化版。</p>
<h2 id="大致流程">大致流程</h2>
<h3 id="确定程序运行依赖">确定程序运行依赖</h3>
<p>从正常跑的环境找一个服务进程,获取其PID后,获取需要依赖的库,并记录下来。以下流程以MacOS环境来举例(懒癌晚期),在Linux环境中依赖为<code class="highlighter-rouge">.so</code>。</p>
<pre><code class="language-log">➜ ~ ps aux | grep rails
weihl 99276 0.0 0.0 4333252 340 s002 S+ Tue05PM 0:00.45 /Users/weihl/.rvm/rubies/ruby-2.1.9/bin/ruby bin/rails c
➜ ~ lsof -p 99276
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ruby 99276 weihl txt REG 1,4 8960 4303138723 /Users/weihl/.rvm/rubies/ruby-2.1.9/bin/ruby
ruby 99276 weihl txt REG 1,4 2910948 4303138725 /Users/weihl/.rvm/rubies/ruby-2.1.9/lib/libruby.2.1.0.dylib
ruby 99276 weihl txt REG 1,4 422620 4303467382 /usr/local/Cellar/gmp/6.1.2_2/lib/libgmp.10.dylib
</code></pre>
<p>从结果可以得到当前运行除了Ruby自身运行库外还依赖<code class="highlighter-rouge">gmp</code>包,那么运行环境就需要包含<code class="highlighter-rouge">gmp</code>,版本也要一致。</p>
<p>这种方式得到的依赖是当前已经在使用的,还有一些是动态加载的,比如字体,图片处理(imagemagick)等,需要慢慢完善。如果能直接用<code class="highlighter-rouge">ldd</code>对应用找依赖就更好啦。</p>
<h3 id="确定程序编译期依赖">确定程序编译期依赖</h3>
<p>这个直接问研发最快啦。比如我们应用就依赖开发工具包(debian: build-essential),libpq-dev,git,还有一大堆的gems。</p>
<h3 id="发布阶段">发布阶段</h3>
<p>利用包含编译期依赖的镜像进行打包,完成后将最终文件拷贝到由运行依赖的镜像中并提交到内部仓库。</p>
<h2 id="dockerfile实现">Dockerfile实现</h2>
<h3 id="base_image">base_image</h3>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dockerfiles/base_image</span>
<span class="k">FROM</span><span class="s"> ruby:2.3-slim-stretch</span>
<span class="k">ENV</span><span class="s"> LC_ALL=C.UTF-8 \</span>
LANG=C.UTF-8 \
LANGUAGE=en_US:en
<span class="k">RUN </span>apt update <span class="nt">-y</span> <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt <span class="nb">install</span> <span class="nt">--no-install-recommends</span> <span class="nt">-y</span> libpq5 wget curl vim gnupg2 libxrender1 <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt <span class="nb">install</span> <span class="nt">--no-install-recommends</span> <span class="nt">-y</span> file tzdata imagemagick gsfonts ttf-wqy-zenhei <span class="se">\
</span> <span class="o">&amp;&amp;</span> <span class="nb">ln</span> <span class="nt">-sf</span> /usr/share/zoneinfo/Asia/Shanghai /etc/localtime <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt clean
<span class="k">CMD</span><span class="s"> [ "irb" ]</span>
</code></pre></div></div>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> ruby_base_image <span class="nt">-f</span> ./dockerfiles/base_image <span class="nb">.</span>
</code></pre></div></div>
<h3 id="builder_image">builder_image</h3>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dockerfiles/builder_image</span>
<span class="k">FROM</span><span class="s"> ruby_base_image</span>
<span class="k">ENV</span><span class="s"> RAILS_ENV=production \</span>
BUNDLE_WITHOUT=development:test
<span class="k">RUN </span>apt update <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt <span class="nb">install</span> <span class="nt">-y</span> build-essential libpq-dev <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt <span class="nb">install</span> <span class="nt">-y</span> git <span class="se">\
</span> <span class="o">&amp;&amp;</span> apt clean
<span class="k">ADD</span><span class="s"> ssh /root/.ssh</span>
<span class="k">RUN </span><span class="nb">chmod </span>700 /root/.ssh <span class="se">\
</span> <span class="o">&amp;&amp;</span> <span class="nb">chmod </span>600 /root/.ssh/id_rsa<span class="k">*</span> <span class="se">\
</span> <span class="o">&amp;&amp;</span> ssh-keyscan my.gitlab.service.com <span class="o">&gt;</span> ~/.ssh/known_hosts
<span class="k">WORKDIR</span><span class="s"> /opt/my_apps</span>
<span class="c"># 以下两个步骤缓存gems,那编译期只需要增量更新即可,定期更新该镜像</span>
<span class="k">ADD</span><span class="s"> Gemfile* ./</span>
<span class="k">RUN </span>bundle config <span class="nt">--global</span> frozen 1 <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle <span class="nb">install</span> <span class="nt">-j4</span> <span class="nt">--retry</span> 3 <span class="se">\
</span> <span class="o">&amp;&amp;</span> <span class="nb">rm</span> <span class="nt">-rf</span> Gemfile<span class="k">*</span>
<span class="k">CMD</span><span class="s"> [ "irb" ]</span>
</code></pre></div></div>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> ruby_builder_image <span class="nt">-f</span> ./dockerfiles/builder_image <span class="nb">.</span>
</code></pre></div></div>
<h3 id="release_image">release_image</h3>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># dockerfiles/release_image</span>
<span class="k">FROM</span><span class="s"> ruby_builder_image as Builder</span>
<span class="k">ENV</span><span class="s"> BUNDLE_WITHOUT=development:test</span>
<span class="k">WORKDIR</span><span class="s"> /opt/my_apps</span>
<span class="k">RUN </span><span class="nb">mkdir</span> <span class="nt">-p</span> tmp/pids tmp/sockets <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle config <span class="nt">--global</span> frozen 1 <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle <span class="nb">install</span> <span class="nt">-j4</span> <span class="nt">--retry</span> 3 <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle <span class="nb">exec </span>rake assets:clean[0] <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle <span class="nb">exec </span>rake assets:precompile <span class="se">\
</span> <span class="o">&amp;&amp;</span> bundle <span class="nb">exec </span>rake log:clear
<span class="k">FROM</span><span class="s"> ruby_base_image</span>
<span class="k">RUN </span>addgroup <span class="nt">--gid</span> 6666 my_app_group <span class="se">\
</span> <span class="o">&amp;&amp;</span> adduser <span class="nt">--uid</span> 6666 <span class="nt">--gid</span> 6666 <span class="nt">--disabled-password</span> <span class="nt">--gecos</span> <span class="s2">"Application"</span> my_app_user
<span class="k">USER</span><span class="s"> my_app_user</span>
<span class="k">COPY</span><span class="s"> --from=Builder /usr/local/bundle/ /usr/local/bundle/</span>
<span class="k">COPY</span><span class="s"> --from=Builder --chown=my_app_user:my_app_group /opt/my_apps /opt/my_apps</span>
<span class="k">ENV</span><span class="s"> RAILS_LOG_TO_STDOUT=true</span>
<span class="k">WORKDIR</span><span class="s"> /opt/my_apps</span>
<span class="k">EXPOSE</span><span class="s"> 8080</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["/bin/sh", "./entrypoint.sh"]</span>
<span class="k">CMD</span><span class="s"> [ "server" ]</span>
</code></pre></div></div>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> ruby_release_image <span class="nt">-f</span> ./dockerfiles/release_image <span class="nb">.</span>
</code></pre></div></div>
<h2 id="协调全部流程">协调全部流程</h2>
<p>通过编写Makefile将以上脚本全部组织起来,部分参数抽取为参数传入。</p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">NAME</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">basename</span> <span class="nt">-s</span> <span class="nb">.</span> <span class="sb">`</span>git rev-parse <span class="nt">--show-toplevel</span><span class="sb">`</span><span class="nv">)</span>
<span class="nv">GIT_COMMIT</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">shell</span> git rev-parse <span class="nt">--short</span> HEAD<span class="nv">)</span>
<span class="nv">GIT_BRANCH</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">shell</span> git rev-parse <span class="nt">--abbrev-ref</span> HEAD<span class="nv">)</span>
<span class="nv">DOCKER_BASE_NAME</span> <span class="o">=</span> reocar-ruby-2.3:stretch
<span class="nv">DOCKER_BUILDER_NAME</span> <span class="o">=</span> reocar-ruby-2.3:builder
<span class="nv">APP_ROOT</span> <span class="o">=</span> /opt/<span class="nv">$(NAME)</span>
<span class="nv">CLEAN_IMAGES</span> <span class="o">:=</span>
<span class="nv">BUILD_TAG</span> <span class="o">:=</span> <span class="nv">$(tag)</span>
<span class="nv">BUNDLE_WITHOUT</span> <span class="o">:=</span> <span class="s2">"development:test"</span>
<span class="c"># check if base images exist
</span>
<span class="err">ifeq</span> <span class="err">(</span><span class="s2">"$(shell docker images -q $(DOCKER_BASE_NAME) 2&gt; /dev/null)"</span><span class="err">,</span><span class="s2">""</span><span class="err">)</span>
<span class="nv">BUILD_BASE_IMAGE</span> <span class="o">=</span> docker build <span class="nt">-t</span> <span class="nv">$(DOCKER_BASE_NAME)</span> <span class="nt">-f</span> ./dockerfiles/base_image .
<span class="err">else</span>
<span class="nv">CLEAN_IMAGES</span> <span class="o">:=</span> <span class="nv">$(CLEAN_IMAGES)</span> <span class="nv">$(DOCKER_BASE_NAME)</span>
<span class="nv">BUILD_BASE_IMAGE</span> <span class="o">=</span>
<span class="err">endif</span>
<span class="c"># check if builder images exist
</span>
<span class="err">ifeq</span> <span class="err">(</span><span class="s2">"$(shell docker images -q $(DOCKER_BUILDER_NAME) 2&gt; /dev/null)"</span><span class="err">,</span><span class="s2">""</span><span class="err">)</span>
<span class="nv">BUILD_BUILDER_IMAGE</span> <span class="o">=</span> docker build <span class="nt">--build-arg</span> <span class="nv">FROM_IMAGE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(DOCKER_BASE_NAME)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">APP_ROOT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(APP_ROOT)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">BUNDLE_WITHOUT</span><span class="o">=</span><span class="nv">$(BUNDLE_WITHOUT)</span> <span class="se">\</span>
<span class="nt">-t</span> <span class="nv">$(DOCKER_BUILDER_NAME)</span> <span class="se">\</span>
<span class="nt">-f</span> ./dockerfiles/builder_image .
<span class="err">else</span>
<span class="nv">CLEAN_IMAGES</span> <span class="o">:=</span> <span class="nv">$(CLEAN_IMAGES)</span> <span class="nv">$(DOCKER_BUILDER_NAME)</span>
<span class="nv">BUILD_BUILDER_IMAGE</span> <span class="o">=</span>
<span class="err">endif</span>
<span class="nv">CLEAN_IMAGES</span> <span class="o">:=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">echo</span> <span class="nv">$(CLEAN_IMAGES)</span> | xargs<span class="nv">)</span>
<span class="err">ifeq</span> <span class="err">(</span><span class="s2">"$(CLEAN_IMAGES)"</span><span class="err">,</span><span class="s2">""</span><span class="err">)</span>
<span class="nv">CLEAN_DOCKER_IMAGES</span> <span class="o">=</span>
<span class="err">else</span>
<span class="nv">CLEAN_DOCKER_IMAGES</span> <span class="o">=</span> docker rmi <span class="nv">$(CLEAN_IMAGES)</span>
<span class="err">endif</span>
<span class="err">ifeq</span> <span class="err">(</span><span class="s2">"$(BUILD_TAG)"</span><span class="err">,</span><span class="s2">""</span><span class="err">)</span>
<span class="nv">BUILD_TAG</span> <span class="o">:=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">date</span> +%Y%m%d%H%M<span class="nv">)</span>
<span class="err">endif</span>
<span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">docker base builder cpssh clean</span>
<span class="nv">.DEFAULT_GOAL</span> <span class="o">:=</span> docker
<span class="nl">docker</span><span class="o">:</span> <span class="nf">base builder; $(info ======== build $(NAME) release image:)</span>
docker build <span class="nt">--build-arg</span> <span class="nv">RUNTIME_IMAGE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(DOCKER_BASE_NAME)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">BUILDER_IMAGE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(DOCKER_BUILDER_NAME)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">FOLDERS_TO_REMOVE</span><span class="o">=</span><span class="s2">"spec node_modules"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">BUNDLE_WITHOUT</span><span class="o">=</span><span class="nv">$(BUNDLE_WITHOUT)</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">EXECJS_RUNTIME</span><span class="o">=</span>Disabled <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">RAILS_ENV</span><span class="o">=</span>production <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">NODE_ENV</span><span class="o">=</span>production <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">APP_ROOT</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(APP_ROOT)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">--build-arg</span> <span class="nv">APP_VERSION</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(NAME)</span><span class="s2">,</span><span class="nv">$(GIT_BRANCH)</span><span class="s2">,</span><span class="nv">$(GIT_COMMIT)</span><span class="s2">"</span> <span class="se">\</span>
<span class="nt">-t</span> <span class="nv">$(NAME)</span>:<span class="nv">$(BUILD_TAG)</span> <span class="se">\</span>
<span class="nt">--rm</span> <span class="nt">-f</span> ./dockerfiles/release_image .
<span class="nl">base</span><span class="o">:</span> <span class="nf">; $(info ======== build $(NAME) runtime image:)</span>
<span class="p">@</span><span class="nv">$(BUILD_BASE_IMAGE)</span>
<span class="nl">builder</span><span class="o">:</span> <span class="nf">cpssh; $(info ======== build $(NAME) compile image:)</span>
<span class="p">@</span><span class="nv">$(BUILD_BUILDER_IMAGE)</span>
<span class="nl">cpssh</span><span class="o">:</span>
<span class="p">@</span><span class="nb">mkdir</span> <span class="nt">-p</span> ssh
<span class="p">@</span><span class="nb">cp</span> <span class="nt">-R</span> ~/.ssh/id_rsa<span class="k">*</span> ssh/
<span class="nl">clean</span><span class="o">:</span> <span class="nf">; $(info ======== clean docker images: $(CLEAN_IMAGES))</span>
<span class="p">@</span><span class="nv">$(CLEAN_DOCKER_IMAGES)</span>
<span class="p">@</span><span class="nb">rm</span> <span class="nt">-rf</span> ssh
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>当通过多段镜像实现部分流程的显式缓存,避免大量重复网络请求,从而实现镜像打包加速。最终镜像仅包含程序文件和运行时文件,大大降低镜像的体积。</p>
<p>缓存镜像需要定期更新,或者依赖发生变化时清除旧的缓存镜像重新打包。</p>
<hr />
<p>欢迎跟我交流 <a href="https://github.com/archfish/archfish" title="archfish blog">Archfish</a></p>
</description>
<pubDate>Thu, 10 Jan 2019 15:55:16 +0800</pubDate>
<link>https://archfish.top/2019/01/10/ruby-on-rails-containerization-03/</link>
<guid isPermaLink="true">https://archfish.top/2019/01/10/ruby-on-rails-containerization-03/</guid>
<category>Ruby</category>
<category>Rails</category>
<category>Docker</category>
<category>Kubernetes</category>
</item>
<item>
<title>Ruby on Rails容器化实践(二)</title>
<description><p>镜像的大小对容器运行资源占用并没有什么太大影响,多层次的镜像结构使得相同layout在每台docker主机上都是唯一一份,即Docker Images共享相同layout。选择<a href="https://alpinelinux.org/" title="alpine">alpine</a>只是因为它易于使用且相关软件包都比较新。本文使用三个Dockerfile来分别处理Rails打包的三个阶段。</p>
<h1 id="运行时镜像">运行时镜像</h1>
<p>即把Rails编译完成后的文件拷贝到该镜像上应当可以正常运行,无依赖问题。这里首先将Alpine构建为满足运行条件的镜像,然后将运行状态的Rails所依赖的包进行整理。</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ps -ef | grep puma</span>
501 57757 868 0 4:41下午 ttys000 0:06.76 puma 3.11.0 <span class="o">(</span>tcp://0.0.0.0:3000<span class="o">)</span> <span class="o">[</span>reocar_store]
501 60614 57757 0 10:16下午 ttys000 0:00.46 puma: cluster worker 0: 57757 <span class="o">[</span>reocar_store]
<span class="c"># lsof -p 60614 | grep .so | grep -v ruby</span>
ruby 30119 rails mem REG 252,0 27000 45613481 /lib/x86_64-linux-gnu/libnss_dns-2.23.so
ruby 30119 rails mem REG 252,0 89696 45613586 /lib/x86_64-linux-gnu/libgcc_s.so.1
</code></pre></div></div>
<p>将库文件放到<a href="https://pkgs.alpinelinux.org/contents" title="alpine contents search">alpine包查询</a>里查出相应软件包,这样我们的运行时镜像就可以用下面的脚本进行打包了:</p>
<pre><code class="language-Dockerfile"># dockerfiles/base_image
ARG FROM_IMAGE=ruby:2.3-alpine
FROM $FROM_IMAGE
LABEL maintainer=$MAINTAINER
ENV GEM_HOME /usr/local/bundle
ENV BUNDLE_PATH="$GEM_HOME" \
BUNDLE_SILENCE_ROOT_WARNING=1 \
BUNDLE_APP_CONFIG="$GEM_HOME"
ENV PATH $GEM_HOME/bin:$BUNDLE_PATH/gems/bin:$PATH
RUN echo 'gem: --no-document' &gt;&gt; /usr/local/etc/gemrc \
&amp;&amp; gem source --remove https://rubygems.org/ --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ \
&amp;&amp; gem install rubygems-update \
&amp;&amp; update_rubygems \
&amp;&amp; gem update --system \
&amp;&amp; rm -rf /root/.gem/specs/* \
&amp;&amp; rm -rf $GEM_HOME/doc/* \
&amp;&amp; rm -rf $GEM_HOME/cache/* \
&amp;&amp; mv /etc/apk/repositories /etc/apk/repositories-bak \
&amp;&amp; { \
echo 'https://mirrors.aliyun.com/alpine/v3.7/main'; \
echo 'https://mirrors.aliyun.com/alpine/v3.7/community'; \
echo '@edge https://mirrors.aliyun.com/alpine/edge/main'; \
echo '@testing https://mirrors.aliyun.com/alpine/edge/testing'; \
echo '@community https://mirrors.aliyun.com/alpine/edge/community'; \
} &gt;&gt; /etc/apk/repositories \
&amp;&amp; apk add --update --no-cache \
libgcc libstdc++ freetds libsasl libldap libpq musl \
file tzdata imagemagick nodejs ghostscript-fonts busybox-suid \
&amp;&amp; ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
CMD [ "irb" ]
</code></pre>
<p>因为我们有新旧系统共同使用,所以安装了mssql server的相关驱动。</p>
<h1 id="编译时包">编译时包</h1>
<pre><code class="language-Dockerfile"># dockerfiles/builder_image
ARG FROM_IMAGE
FROM $FROM_IMAGE
ARG APP_ROOT
ARG BUNDLE_WITHOUT
ENV RAILS_ENV=production
ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT}
RUN apk add --update --no-cache \
openssh-client build-base git \
freetds-dev postgresql-dev \
postgis@testing libressl2.7-libcrypto@edge json-c@edge
ADD ssh /root/.ssh
RUN chmod 700 /root/.ssh \
&amp;&amp; chmod 600 /root/.ssh/id_rsa* \
&amp;&amp; ssh-keyscan my.git.server.com &gt; ~/.ssh/known_hosts
WORKDIR ${APP_ROOT}
ADD Gemfile* ./
RUN bundle config --global frozen 1 \
&amp;&amp; bundle install -j4 --retry 3 \
&amp;&amp; find /usr/local/bundle/gems/ -name wkhtmltopdf_darwin* -delete \
&amp;&amp; find /usr/local/bundle/gems/ -name wkhtmltopdf_linux_x86 -delete \
&amp;&amp; rm -rf Gemfile*
CMD [ "irb" ]
</code></pre>
<p>这里将开发组件全部安装好,并配置好ssh用于拉取项目源码。同时缓存Gemfile内使用的包,这样每次可以省下不少时间。</p>
<h1 id="发行包">发行包</h1>
<pre><code class="language-Dockerfile"># dockerfiles/release
ARG BUILDER_IMAGE
ARG RUNTIME_IMAGE
FROM $BUILDER_IMAGE as Builder
ARG FOLDERS_TO_REMOVE
ARG BUNDLE_WITHOUT
ARG RAILS_ENV
ARG NODE_ENV
ARG APP_ROOT
ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT}
ENV RAILS_ENV ${RAILS_ENV}
ENV NODE_ENV ${NODE_ENV}
ENV SECRET_KEY_BASE=foo
WORKDIR ${APP_ROOT}
COPY ./ ./
RUN mkdir -p ~/.ssh \
&amp;&amp; chmod 700 ~/.ssh \
&amp;&amp; cp ssh/* ~/.ssh/ \
&amp;&amp; chmod 600 ~/.ssh/id_rsa* \
&amp;&amp; ssh-keyscan my.git.server.com &gt; ~/.ssh/known_hosts
RUN mkdir -p tmp/pids tmp/sockets \
&amp;&amp; bundle config --global frozen 1 \
&amp;&amp; bundle install -j4 --retry 3 \
&amp;&amp; bundle exec rake assets:clean[0] \
&amp;&amp; bundle exec rake assets:precompile \
&amp;&amp; bundle exec rake tmp:clear \
&amp;&amp; bundle exec rake log:clear \
&amp;&amp; rm -rf /usr/local/bundle/cache/*.gem \
&amp;&amp; find /usr/local/bundle/gems/ -name "*.c" -delete \
&amp;&amp; find /usr/local/bundle/gems/ -name "*.o" -delete \
&amp;&amp; rm -rf $FOLDERS_TO_REMOVE
FROM $RUNTIME_IMAGE
ARG EXECJS_RUNTIME
ARG APP_ROOT
ARG RAILS_ENV
ARG APP_VERSION
RUN addgroup -g 1001 -S rails \
&amp;&amp; adduser -u 1001 -S rails -G rails
USER rails
COPY --from=Builder /usr/local/bundle/ /usr/local/bundle/
COPY --from=Builder --chown=rails:rails ${APP_ROOT} ${APP_ROOT}
ENV RAILS_ENV=${RAILS_ENV} \
RAILS_LOG_TO_STDOUT=true \
RAILS_SERVE_STATIC_FILES=true \
EXECJS_RUNTIME=$EXECJS_RUNTIME \
APP_VERSION=$APP_VERSION \
SECRET_KEY_BASE=7dea1************************************************8257dfe7fc
WORKDIR ${APP_ROOT}
EXPOSE 3000
ENTRYPOINT ["/bin/sh", "./entrypoint.sh"]
CMD [ "server" ]
</code></pre>
<p>这里有两个部分,第一部分负责使用「编译时包」对源码进行编译,编译完成后将多余文件清理干净,第二部分直接将bundler的安装目录和项目编译后的文件拷贝到「运行时镜像」,并设置相关环境变量即可。</p>
<h1 id="makefile">Makefile</h1>
<p>每次靠人力去维护这些脚本,并组合使用非常麻烦,需要写一大堆的使用文档才能让后来的人去使用。这里我们引入<a href="http://www.ruanyifeng.com/blog/2015/02/make.html" title="Make 命令教程">Makefile</a>来帮我组织这些命令和脚本。这个脚本应当达到以下目的</p>
<ul>
<li>镜像打包以后不再携带git相关信息,所以需要获取当前的版本,版本由<code class="highlighter-rouge">项目名+分支名+当前提交号组成</code>,这样方便查看当前代码是否正确</li>
<li>各阶段生成镜像名应当在编译时统一指定,这样可以使用已经生成的镜像进行操作,可以节省一大堆时间</li>
<li>指定项目运行目录,将目录从dockerfile中解放出来,便于维护</li>
<li>各种环境变量也应当由Makefile进行控制,可以灵活修改</li>
<li>最终生成的镜像tag应当能定制,方便与CI/CD工具整合</li>
</ul>
<pre><code class="language-Makefile">NAME = $(shell basename -s . `git rev-parse --show-toplevel`)
GIT_COMMIT = $(shell git rev-parse --short HEAD)
GIT_BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
DOCKER_BASE_NAME = reocar-ruby-2.3:alpine3.7
DOCKER_BUILDER_NAME = reocar-ruby-2.3:builder
APP_ROOT = /opt/$(NAME)
CLEAN_IMAGES :=
BUILD_TAG := $(tag)
BUNDLE_WITHOUT := "development:test"
# check if base images exist
ifeq ("$(shell docker images -q $(DOCKER_BASE_NAME) 2&gt; /dev/null)","")
BUILD_BASE_IMAGE = docker build -t $(DOCKER_BASE_NAME) -f ./dockerfiles/base_image .
else
CLEAN_IMAGES := $(CLEAN_IMAGES) $(DOCKER_BASE_NAME)
BUILD_BASE_IMAGE =
endif
# check if builder images exist
ifeq ("$(shell docker images -q $(DOCKER_BUILDER_NAME) 2&gt; /dev/null)","")
BUILD_BUILDER_IMAGE = docker build --build-arg FROM_IMAGE="$(DOCKER_BASE_NAME)" \
--build-arg APP_ROOT="$(APP_ROOT)" \
--build-arg BUNDLE_WITHOUT=$(BUNDLE_WITHOUT) \
-t $(DOCKER_BUILDER_NAME) \
-f ./dockerfiles/builder_image .
else
CLEAN_IMAGES := $(CLEAN_IMAGES) $(DOCKER_BUILDER_NAME)
BUILD_BUILDER_IMAGE =
endif
CLEAN_IMAGES := $(shell echo $(CLEAN_IMAGES) | xargs)
ifeq ("$(CLEAN_IMAGES)","")
CLEAN_DOCKER_IMAGES =
else
CLEAN_DOCKER_IMAGES = docker rmi $(CLEAN_IMAGES)
endif
ifeq ("$(BUILD_TAG)","")
BUILD_TAG := $(shell date +%Y%m%d%H%M)
endif
.PHONY: docker base builder cpssh clean
.DEFAULT_GOAL := docker
docker: base builder; $(info ======== build $(NAME) release image:)
docker build --build-arg RUNTIME_IMAGE="$(DOCKER_BASE_NAME)" \
--build-arg BUILDER_IMAGE="$(DOCKER_BUILDER_NAME)" \
--build-arg FOLDERS_TO_REMOVE="spec node_modules app/assets vendor/assets lib/assets" \
--build-arg BUNDLE_WITHOUT=$(BUNDLE_WITHOUT) \
--build-arg EXECJS_RUNTIME=Disabled \
--build-arg RAILS_ENV=production \
--build-arg NODE_ENV=production \
--build-arg APP_ROOT="$(APP_ROOT)" \
--build-arg APP_VERSION="$(NAME),$(GIT_BRANCH),$(GIT_COMMIT)" \
-t $(NAME):$(BUILD_TAG) \
--rm -f ./dockerfiles/release .
base: ; $(info ======== build $(NAME) runtime image:)
@$(BUILD_BASE_IMAGE)
builder: cpssh; $(info ======== build $(NAME) compile image:)
@$(BUILD_BUILDER_IMAGE)
cpssh:
@cp -R ~/.ssh ssh
clean: ; $(info ======== clean docker images: $(CLEAN_IMAGES))
@$(CLEAN_DOCKER_IMAGES)
@rm -rf ssh
</code></pre>
<p>使用方法:在项目根目录敲下<code class="highlighter-rouge">make</code>即可,如果需要清理缓存镜像,使用<code class="highlighter-rouge">make clean</code></p>
<p>注意!!</p>
<p>请不要执行 <code class="highlighter-rouge">/bin/sh Makefile</code> 命令,这样既不是Makefile的正确用法,也无法预测会发生什么事情。<a href="https://github.com/fagongzi/gateway/issues/93" title="什么鬼,运行了根目录下的Makefile,自动删除了我好多文件夹,包括了根目录的文件">从前有个人执行了这个命令,然后他再也没出现过了- -||</a></p>
<hr />
<p>欢迎跟我交流 <a href="https://github.com/archfish/archfish" title="archfish blog">Archfish</a></p>
</description>
<pubDate>Mon, 20 Aug 2018 22:45:51 +0800</pubDate>
<link>https://archfish.top/2018/08/20/ruby-on-rails-containerization-02/</link>
<guid isPermaLink="true">https://archfish.top/2018/08/20/ruby-on-rails-containerization-02/</guid>
<category>Ruby</category>
<category>Rails</category>
<category>Docker</category>
<category>Kubernetes</category>
</item>
<item>
<title>Golang开发环境搭建(vs code)</title>
<description><h1 id="组织项目">组织项目</h1>
<p>许多从其它语言转到golang的可能都会有一个疑问:GOPATH是什么?官方的<a href="https://github.com/golang/go/wiki/SettingGOPATH" title="SettingGOPATH">wiki</a>是这样说的:</p>
<blockquote>
<p>The GOPATH environment variable specifies the location of your workspace.</p>
</blockquote>
<p>即GOPATH指向一个工作区,可类比到Linux的文件系统,系统只能访问根(/)下的文件,而根之上是什么就不是系统管理的范围了。回到go语言里,GOPATH下的代码才能被go管理,在这个路径之上的源码一概不理会。</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> GOPATH
____________________|____________________
| | |
src bin pkg
______|_______ ____|____ _____|_____
| | | | | | |
GitHub Gopkg Other Tools Platform Code
</code></pre></div></div>
<p>GOPATH的文件目录如上图所示,src下是以网站命名的文件夹,再往下才是项目目录。通过<code class="highlighter-rouge">go get</code>拉下来的源码就是按这样的层级结构存放的,我们自己的项目也建议保持这样的结构。</p>
<p>以我们公司来讲,我们通常将RoR项目存放在<code class="highlighter-rouge">~/Projects/</code>目录下,那有没有什么法子可以继续保持这样的组织方式还能满足go的组织风格呢?这时候就需要借助<a href="https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html" title="理解 Linux 的硬链接与软链接">软链</a>了,通过软链我们在<code class="highlighter-rouge">~/Projects/</code>下创建一个到GOPATH下相应项目的链接,这样就可以继续按照原来的风格管理go项目了。下图演示如何用vs code打开项目:</p>
<pre><code class="language-log">$ echo $GOPATH
/Users/archfish/GoPath
$ ls -lh ~/Projects
lrwxr-xr-x 1 weihl staff 51B 11 22 2017 gateway -&gt; /Users/archfish/GoPath/src/github.com/fagongzi/gateway
$ cd ~/Projects/gateway &amp;&amp; code .
</code></pre>
<h1 id="配置开发工具">配置开发工具</h1>
<h2 id="用梯子版">用梯子版</h2>
<p>梯子有很多种,我这里拿其中一种已经没人使用的<a href="https://github.com/shadowsocks/shadowsocks" title="Removed according to regulations.">梯子</a>做例子,其它类型梯子也可以按照这个思路操作。该梯子是使用的socket5协议进行通讯的,而shell中只能使用http或https协议,这里需要一个socket5-&gt;http/https的转换(可以试试这个<a href="https://github.com/nybuxtsui/goproxy" title="goproxy">工具</a>我没用过供大家参考)。mac上可以使用一款叫<a href="https://github.com/shadowsocks/ShadowsocksX-NG" title="ShadowsocksX-NG">*X-NG</a>的客户端(&gt;= 1.7),默认就可以转换为http协议,操作方式点击图标找到<code class="highlighter-rouge">Copy HTTP Proxy Shell Export Line</code>然后贴到shell中,然后通过shell启动vs code即可。以我的配置为例:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">http_proxy</span><span class="o">=</span>http://0.0.0.0:1087<span class="p">;</span><span class="nb">export </span><span class="nv">https_proxy</span><span class="o">=</span>http://0.0.0.0:1087<span class="p">;</span>
<span class="nb">cd</span> ~/Projects/gateway <span class="o">&amp;&amp;</span> code <span class="nb">.</span>
</code></pre></div></div>
<ul>
<li>打开vs code<code class="highlighter-rouge">扩展管理</code>搜索go,安装;</li>
<li>按下<code class="highlighter-rouge">shift + command + p</code>(Linux <code class="highlighter-rouge">ctrl + shift + p</code>)找到<code class="highlighter-rouge">Go: Install/Update Tools</code>;</li>
<li>等待安装完成即可</li>
</ul>
<h2 id="不用梯子版">不用梯子版</h2>
<ul>
<li>打开vs code<code class="highlighter-rouge">扩展管理</code>搜索go,安装;</li>
<li>按下<code class="highlighter-rouge">shift + command + p</code>(Linux <code class="highlighter-rouge">ctrl + shift + p</code>)找到<code class="highlighter-rouge">Go: Install/Update Tools</code>;</li>
<li>打开<a href="https://golangtc.com/download/package" title="离线pkg包">网站</a>,将上一步列出的包一个个按教程手动下载并安装</li>
</ul>
<h1 id="额外配置">额外配置</h1>
<h2 id="空白处理">空白处理</h2>
<p>通过简单的配置可以让编辑器帮我们处理一些编辑任务:</p>
<ul>
<li>文件最后保留一个空行</li>
<li>删除文件最后多余空行</li>
<li>清理每行结尾空格制表符等</li>
</ul>
<pre><code class="language-JSON">{
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true
}
</code></pre>
<h2 id="golang-19-alias支持">golang 1.9 alias支持</h2>
<p>通过右键选择<code class="highlighter-rouge">Go to Definition</code>和<code class="highlighter-rouge">Peek Definition</code>在1.9版本后可能会无法正确跳转,这个其实是个<a href="https://github.com/Microsoft/vscode-go/issues/1261" title="go-to-definition doesn't work in go 1.9 with type aliasing.">Bug</a>,如下配置即可:</p>
<pre><code class="language-JSON">{
"go.docsTool": "gogetdoc"
}
</code></pre>
<hr />
<p>欢迎跟我交流 <a href="https://github.com/archfish/archfish" title="archfish blog">Archfish</a></p>
</description>
<pubDate>Thu, 16 Aug 2018 21:57:18 +0800</pubDate>
<link>https://archfish.top/2018/08/16/vs-code-golang-dev/</link>
<guid isPermaLink="true">https://archfish.top/2018/08/16/vs-code-golang-dev/</guid>
<category>Golang</category>
<category>vs code</category>
</item>
<item>
<title>Ruby on Rails文件存储整合S3 API</title>
<description><p>在项目初期我们将一台服务器插满硬盘并提供NFS服务挂载到各业务服务器上。业务产生的文件分为两类,一类是涉及隐私的敏感文件,另一类是可公开访问的普通文件。普通文件需要对接CDN以降低服务器带宽压力。敏感文件只能在相应业务使用到时才能访问,通常是先加载到内存然后再嵌入页面或Base64编码后通过JSON返回给前端。</p>
<p>在业务系统中使用<a href="https://github.com/carrierwaveuploader/carrierwave" title="CarrierWave">CarrierWave</a>作为文件上传管理组件,部分图片相关业务需要生成缩略图或保存多版本文件。</p>
<h2 id="files">files</h2>
<p>对外URL只能处理可公开访问的文件,对于隐私文件只能使用文件操作读取到内存。下面是uploader封装统一URL的例子,其他的uploader继承该class即可,使用时直接使用<code class="highlighter-rouge">external_url</code>。</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">BaseUploader</span> <span class="o">&lt;</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span>
<span class="kp">include</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">MiniMagick</span>
<span class="n">storage</span> <span class="ss">:file</span>
<span class="k">def</span> <span class="nf">external_url</span>
<span class="k">return</span> <span class="k">if</span> <span class="nb">self</span><span class="p">.</span><span class="nf">blank?</span>
<span class="k">return</span> <span class="k">if</span> <span class="n">private_file?</span>
<span class="c1"># 使用最后更新时间作为CDN自动过期方案</span>
<span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">model</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:updated_at</span><span class="p">)</span> <span class="o">||</span> <span class="nb">self</span><span class="p">.</span><span class="nf">model</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:created_at</span><span class="p">)</span> <span class="o">||</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">).</span><span class="nf">tv_sec</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"?t=</span><span class="si">#{</span><span class="n">s</span><span class="si">}</span><span class="s2">"</span>
<span class="n">wrap_cdn</span><span class="p">(</span><span class="n">url</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="no">Rails</span><span class="p">.</span><span class="nf">root</span><span class="p">),</span> <span class="s1">''</span><span class="p">)</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">wrap_cdn</span><span class="p">(</span><span class="n">file_path</span><span class="p">)</span>
<span class="k">if</span> <span class="n">fit_cdn_condition?</span>
<span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">cdn_domain</span><span class="p">,</span> <span class="n">file_path</span><span class="p">)</span>
<span class="k">else</span>
<span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">backend_domain</span><span class="p">,</span> <span class="n">file_path</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<h2 id="aws">aws</h2>
<p>我们是托管服务器,综合对比了几款S3兼容存储软件后决定选择Ceph作为存储服务,考虑到后期可能需要块存储Ceph是个不错的选择。CarrierWave配合<a href="https://github.com/sorentwo/carrierwave-aws" title="carrierwave-aws">carrierwave-aws</a>可以很方便使用<a href="https://github.com/aws/aws-sdk-ruby" title="aws-sdk-ruby">aws-sdk-ruby</a>,假设RGW服务地址为<code class="highlighter-rouge">http://ceph:10086</code>则可以通过下面的方式配置s3</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/initializers/carrierwave.rb</span>
<span class="no">CarrierWave</span><span class="p">.</span><span class="nf">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
<span class="n">config</span><span class="p">.</span><span class="nf">storage</span> <span class="o">=</span> <span class="ss">:aws</span>
<span class="n">config</span><span class="p">.</span><span class="nf">aws_bucket</span> <span class="o">=</span> <span class="ss">:mybucket</span>
<span class="n">config</span><span class="p">.</span><span class="nf">aws_acl</span> <span class="o">=</span> <span class="s1">'public-read'</span>
<span class="c1"># 该参数用于设置图片访问域名</span>
<span class="c1"># config.asset_host = File.join(endpoint, bucket)</span>
<span class="c1"># 隐私文件开放时间,过期则返回403</span>
<span class="n">config</span><span class="p">.</span><span class="nf">aws_authenticated_url_expiration</span> <span class="o">=</span> <span class="mi">30</span><span class="p">.</span><span class="nf">minute</span><span class="p">.</span><span class="nf">to_i</span>
<span class="n">config</span><span class="p">.</span><span class="nf">aws_attributes</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">cache_control: </span><span class="s1">'max-age=604800'</span>
<span class="p">}</span>
<span class="n">config</span><span class="p">.</span><span class="nf">aws_credentials</span> <span class="o">=</span> <span class="p">{</span>
<span class="ss">access_key_id: :access_key</span><span class="p">,</span>
<span class="ss">secret_access_key: :secret_key</span><span class="p">,</span>
<span class="ss">force_path_style: </span><span class="kp">true</span><span class="p">,</span>
<span class="ss">endpoint: </span><span class="s1">'http://ceph:10086'</span><span class="p">,</span>
<span class="ss">region: </span><span class="s1">'us-east-1'</span><span class="p">,</span>
<span class="ss">stub_responses: </span><span class="no">Rails</span><span class="p">.</span><span class="nf">env</span><span class="p">.</span><span class="nf">test?</span> <span class="c1"># Optional, avoid hitting S3 actual during tests</span>
<span class="p">}</span>
<span class="k">end</span>
</code></pre></div></div>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># base_uploader.rb</span>
<span class="k">class</span> <span class="nc">BaseUploader</span> <span class="o">&lt;</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span>
<span class="kp">include</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">MiniMagick</span>
<span class="n">storage</span> <span class="ss">:aws</span>
<span class="c1"># 获取图片外部访问地址,有CDN可用时使用CDN</span>
<span class="k">def</span> <span class="nf">external_url</span>
<span class="k">return</span> <span class="k">if</span> <span class="nb">self</span><span class="p">.</span><span class="nf">blank?</span>
<span class="c1"># 私有文件会带上一些鉴权参数,不能对其进行修改</span>
<span class="k">return</span> <span class="n">url</span> <span class="k">unless</span> <span class="n">public_file?</span>
<span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">model</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:updated_at</span><span class="p">)</span> <span class="o">||</span> <span class="nb">self</span><span class="p">.</span><span class="nf">model</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:created_at</span><span class="p">)</span> <span class="o">||</span> <span class="no">Time</span><span class="p">.</span><span class="nf">current</span><span class="p">).</span><span class="nf">tv_sec</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"?t=</span><span class="si">#{</span><span class="n">s</span><span class="si">}</span><span class="s2">"</span>
<span class="n">url_</span> <span class="o">=</span> <span class="nb">self</span><span class="p">.</span><span class="nf">url</span>
<span class="c1"># 自定义 asset_host 时不做替换</span>
<span class="k">if</span> <span class="nb">self</span><span class="p">.</span><span class="nf">asset_host</span><span class="p">.</span><span class="nf">blank?</span>
<span class="n">url_</span> <span class="o">=</span> <span class="n">url_</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="n">backend_domain</span><span class="p">,</span> <span class="n">cdn_domain</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">url_</span> <span class="o">+</span> <span class="n">s</span>
<span class="k">end</span>
<span class="c1"># 根据文件类型设置文件的访问权限</span>
<span class="c1"># CarrierWave::Uploader::Base::ACCEPTED_ACL</span>
<span class="k">def</span> <span class="nf">aws_acl</span>
<span class="n">public_file?</span> <span class="p">?</span> <span class="s1">'public-read'</span> <span class="p">:</span> <span class="s1">'private'</span>
<span class="k">end</span>
<span class="c1"># 用于处理不同时期不同存放路径问题,无此需求忽略这个方法</span>
<span class="k">def</span> <span class="nf">dir_exists?</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
<span class="c1"># 检查S3存储目录是否存在,其他存储可能不适用</span>
<span class="c1"># NOTE S3存储中,如果目录没有文件则父目录也不存在</span>
<span class="k">if</span> <span class="nb">self</span><span class="p">.</span><span class="nf">_storage</span> <span class="o">==</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Storage</span><span class="o">::</span><span class="no">AWS</span>
<span class="n">bucket</span> <span class="o">=</span> <span class="no">Aws</span><span class="o">::</span><span class="no">S3</span><span class="o">::</span><span class="no">Bucket</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">self</span><span class="p">.</span><span class="nf">aws_bucket</span><span class="p">,</span> <span class="nb">self</span><span class="p">.</span><span class="nf">aws_credentials</span><span class="p">)</span>
<span class="n">bucket</span><span class="p">.</span><span class="nf">objects</span><span class="p">(</span><span class="ss">prefix: </span><span class="n">dir</span><span class="p">).</span><span class="nf">limit</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nf">any?</span>
<span class="k">else</span>
<span class="no">Dir</span><span class="p">.</span><span class="nf">exist?</span><span class="p">(</span><span class="n">dir</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>