OSDN Git Service

improve override conf. management of docker engine.
[metasearch/grid-chef-repo.git] / cookbooks / docker-grid / recipes / engine.rb
1 #
2 # Cookbook Name:: docker-grid
3 # Recipe:: engine
4 #
5 # Copyright 2016-2018, whitestar
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #     http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19
20 # https://docs.docker.com/install/
21
22 platform = node['platform']
23 platform_family = node['platform_family']
24 platform_version = node['platform_version']
25 init_package = node['init_package']
26
27 install_flavor = node['docker-grid']['install_flavor']
28 override_apt_line = node['docker-grid']['apt_repo']['override_apt_line']
29 if !override_apt_line.nil? && !override_apt_line.empty? \
30   && override_apt_line.include?(node['docker-grid']['dockerproject']['apt_new_repo_url'])
31   Chef::Log.warn('This docker-grid::engine recipe uses the Docker new repository by the `override_apt_line` attribute.')
32   node.force_override['docker-grid']['dockerproject']['enable_new_repo'] = true
33 end
34 enable_new_repo = node['docker-grid']['dockerproject']['enable_new_repo']
35
36 engine_conf = node['docker-grid']['engine']
37 docker_ver = engine_conf['version']
38 docker_ver = '' if docker_ver.nil? || docker_ver == 'latest'
39 if docker_ver.empty?
40   Chef::Log.warn('This docker-grid::engine installs the latest Docker engine.')
41 else
42   Chef::Log.info("This docker-grid::engine installs the Docker engine: #{docker_ver}.")
43 end
44 storage_driver = engine_conf['storage-driver']
45 userns_remap = engine_conf['userns-remap']
46
47 if engine_conf['skip_setup']
48   log 'Skip the Docker Engine setup.'
49   return
50 end
51
52 ::Chef::Recipe.send(:include, DockerGrid::Helper)
53 ::Chef::Recipe.send(:include, PlatformUtils::Helper)
54 ::Chef::Recipe.send(:include, PlatformUtils::VirtUtils)
55
56 override_dir_path = '/etc/systemd/system/docker.service.d'
57 override_conf_path = "#{override_dir_path}/override.conf"
58
59 [
60   'bridge-utils',
61 ].each {|pkg|
62   resources(package: pkg) rescue package pkg do
63     action :install
64   end
65 }
66
67 systemctl_daemon_reload = 'systemctl_daemon-reload'
68 resources(bash: systemctl_daemon_reload) rescue bash systemctl_daemon_reload do
69   code <<-EOH
70     systemctl daemon-reload
71   EOH
72   action :nothing
73 end
74
75 # https://docs.docker.com/engine/userguide/storagedriver/selectadriver/
76 if shell_out("cat /etc/mtab | grep -E '\s+/\s+zfs\s+'").exitstatus.zero?
77   if container_guest_node?
78     Chef::Log.warn('This node is running in the Linux container with ZFS, set the storage-driver to vfs as a fallback.')
79     node.override['docker-grid']['engine']['storage-driver'] = 'vfs'
80   else
81     Chef::Log.warn('This node is running on ZFS, set the storage-driver to zfs.')
82     node.override['docker-grid']['engine']['storage-driver'] = 'zfs'
83   end
84 end
85
86 if storage_driver == 'overlay2'
87   if !docker_ver.empty? && gem_ver(docker_ver) < gem_ver('1.12')
88     Chef::Application.fatal!('Docker version must be 1.12 or later for overlay2 storage driver.')  # and exit.
89   end
90 end
91 load_kernel_module('overlay') if storage_driver =~ /overlay2?/
92
93 if !userns_remap.nil? && !userns_remap.empty?
94   if !docker_ver.empty? && gem_ver(docker_ver) < gem_ver('1.10')
95     Chef::Application.fatal!('Docker version must be 1.10 or later for userns-remap.')  # and exit.
96   end
97
98   include_recipe 'platform_utils::kernel_user_namespace'
99
100   remap_user = userns_remap == 'default' ? 'dockremap' : userns_remap
101   notifies_conf = {
102     'action' => :restart,
103     'resource' => 'service[docker]',
104     'timer' => :delayed,
105   }
106   ::Chef::Recipe.send(:include, PlatformUtils::Helper)
107   append_subusers([remap_user], notifies_conf)
108 end
109
110 file override_conf_path do
111   action :nothing
112 end
113
114 bash 'clean_up_docker0_bridge' do
115   code <<-"EOH"
116     if brctl show | grep docker0; then
117       ip link set docker0 down
118       brctl delbr docker0
119     fi
120     # https://github.com/docker/docker/issues/23630
121     if [ -d /var/lib/docker/network ]; then
122       rm -rf /var/lib/docker/network
123     fi
124   EOH
125   action :nothing
126 end
127
128 case platform_family
129 when 'rhel'
130   ex_enablerepo = node['docker-grid']['dockerproject']['yum_new_repo_extra_enablerepo']
131
132   if storage_driver == 'devicemapper'
133     [
134       #'yum-utils',
135       'device-mapper-persistent-data',
136       'lvm2',
137     ].each {|pkg|
138       resources(package: pkg) rescue package pkg do
139         action :install
140       end
141     }
142   end
143
144   if install_flavor == 'dockerproject'
145     # https://dcos.io/docs/1.8/administration/installing/custom/system-requirements/install-docker-centos/
146     old_repo_action = nil
147     new_repo_action = nil
148     pkgs = nil
149     if enable_new_repo
150       old_repo_action = :delete
151       new_repo_action = :create
152       pkgs = [
153         'docker-ce',
154       ]
155     else
156       old_repo_action = :create
157       new_repo_action = :delete
158       pkgs = [
159         'docker-engine-selinux',
160         'docker-engine',
161       ]
162     end
163
164     [
165       'docker.repo',
166       'docker-ce.repo',
167     ].each {|repo_file|
168       template "/etc/yum.repos.d/#{repo_file}" do
169         source  "etc/yum.repos.d/#{repo_file}"
170         owner 'root'
171         group 'root'
172         mode '0644'
173         action repo_file == 'docker.repo' ? old_repo_action : new_repo_action
174       end
175     }
176
177     [
178       'docker',
179       'docker-common',
180       #'container-selinux',
181       'docker-selinux',
182     ].each {|pkg|
183       resources(package: pkg) rescue package pkg do
184         action platform_family == 'debian' ? :purge : :remove
185         notifies :delete, "file[#{override_conf_path}]", :immediately
186         notifies :run, 'bash[clean_up_docker0_bridge]', :immediately
187       end
188     }
189
190     # each repo of the new 'docker-ce-stable' and the old 'dockerrepo' is disabled by default to prevent automatic update.
191     enablerepos = enable_new_repo ? ['docker-ce-stable'] : ['dockerrepo']
192     enablerepos.push(ex_enablerepo) if enable_new_repo && !ex_enablerepo.nil? && !ex_enablerepo.empty?
193     pkgs.each {|pkg|
194       resources(yum_package: pkg) rescue yum_package pkg do
195         allow_downgrade true
196         action :install
197         version docker_ver unless docker_ver.empty?
198         options "--enablerepo=#{enablerepos.join(',')}"
199         if pkg == 'docker-engine' || pkg == 'docker-ce'
200           notifies :run, 'bash[clean_up_docker0_bridge]', :before
201           # for beaking change
202           notifies :create, "template[#{override_conf_path}]", :before if init_package == 'systemd'
203         end
204       end
205     }
206   else
207     # OS distribution
208     [
209       'docker-engine-selinux',
210       'docker-engine',
211     ].each {|pkg|
212       resources(package: pkg) rescue package pkg do
213         action platform_family == 'debian' ? :purge : :remove
214         notifies :delete, "file[#{override_conf_path}]", :immediately
215         notifies :run, 'bash[clean_up_docker0_bridge]', :immediately
216       end
217     }
218
219     [
220       'docker',
221     ].each {|pkg|
222       resources(yum_package: pkg) rescue yum_package pkg do
223         allow_downgrade true
224         action :install
225         version docker_ver unless docker_ver.empty?
226         notifies :run, 'bash[clean_up_docker0_bridge]', :before
227       end
228     }
229
230     template '/etc/sysconfig/docker' do
231       source  'etc/sysconfig/docker'
232       owner 'root'
233       group 'root'
234       mode '0644'
235       notifies :restart, 'service[docker]'
236     end
237   end
238 when 'debian'
239   # https://docs.docker.com/engine/installation/linux/debian/
240   # https://docs.docker.com/engine/installation/linux/ubuntulinux/
241   pkgs = [
242     'apt-transport-https',
243     'ca-certificates',
244     'curl',
245     'gnupg2',
246     'software-properties-common',
247   ]
248
249   if storage_driver == 'aufs' \
250     && !container_guest_node?
251     if platform == 'debian'
252       pkgs += [
253         'aufs-dkms',
254       ]
255     elsif platform == 'ubuntu'
256       pkgs += [
257         "linux-image-extra-#{node['os_version']}",
258         'linux-image-extra-virtual',
259       ]
260     end
261   end
262
263   pkgs.each {|pkg|
264     resources(package: pkg) rescue package pkg do
265       action :install
266     end
267   }
268
269   apt_get_update = 'apt-get_update'
270   resources(execute: apt_get_update) rescue execute apt_get_update do
271     command 'apt-get update'
272     action :nothing
273   end
274
275   if install_flavor == 'dockerproject'
276     pkg_name_removed = 'docker.io'
277     pkg_name = node['docker-grid']['dockerproject']['package_name']
278
279     apt_repo_config = node['docker-grid']['apt_repo']
280     apt_key_add_cmd = nil
281     apt_key_name = nil
282     if enable_new_repo
283       apt_key_add_cmd = "curl -fsSL https://download.docker.com/linux/#{platform}/gpg | apt-key add -"
284       apt_key_name = 'Docker Release (CE deb)'
285     else
286       apt_key_add_cmd = "apt-key adv --keyserver #{apt_repo_config['keyserver']} --recv-keys #{apt_repo_config['recv-keys']}"
287       apt_key_name = 'Docker Release Tool (releasedocker)'
288     end
289     bash 'apt-key_adv_docker_tools_key' do
290       code <<-"EOH"
291         #{apt_key_add_cmd}
292       EOH
293       action :nothing
294       not_if "apt-key list 2>&1 | grep '#{apt_key_name}'"
295     end
296
297     template '/etc/apt/sources.list.d/docker.list' do
298       source  'etc/apt/sources.list.d/docker.list'
299       owner 'root'
300       group 'root'
301       mode '0644'
302       helpers Chef::Mixin::ShellOut
303       notifies :run, 'bash[apt-key_adv_docker_tools_key]', :before
304       notifies :run, "execute[#{apt_get_update}]", :immediately
305     end
306   else
307     # OS distribution
308     pkg_name_removed = node['docker-grid']['dockerproject']['package_name']
309     pkg_name = 'docker.io'
310   end
311
312   # Pinning Docker version
313   template '/etc/apt/preferences.d/docker.pref' do
314     source  'etc/apt/preferences.d/docker.pref'
315     owner 'root'
316     group 'root'
317     mode '0644'
318     action :delete if docker_ver.empty?
319     variables(
320       pkg_name: pkg_name
321     )
322   end
323
324   resources(package: pkg_name_removed) rescue package pkg_name_removed do
325     action platform_family == 'debian' ? :purge : :remove
326     notifies :delete, "file[#{override_conf_path}]", :immediately
327     notifies :run, 'bash[clean_up_docker0_bridge]', :immediately
328   end
329
330   resources(package: pkg_name) rescue package pkg_name do
331     action :install
332     options "-o Dpkg::Options::='--force-confnew'" if platform_family == 'debian'
333     options '--allow-downgrades' if platform == 'debian' || platform_version >= '16.04'  # LTS (xenial)
334     options '--force-yes' if platform_version == '14.04'  # LTS (trusty)
335     version docker_ver unless docker_ver.empty?
336     notifies :run, 'bash[clean_up_docker0_bridge]', :before
337     notifies :run, 'bash[apt-key_adv_docker_tools_key]', :before
338     notifies :run, "execute[#{apt_get_update}]", :before
339     # for beaking change
340     notifies :create, "template[#{override_conf_path}]", :before if init_package == 'systemd'
341   end
342 end
343
344 docker_opts = []
345 docker_opts.push("--storage-driver=#{storage_driver}") if !storage_driver.nil? && !storage_driver.empty?
346 docker_opts.push("--userns-remap=#{userns_remap}") if !userns_remap.nil? && !userns_remap.empty?
347
348 extra_options = engine_conf['daemon_extra_options']
349 # for docker-engine package on RHEL: remove '-H fd://'
350 # https://github.com/docker/docker/issues/22847
351 if platform_family == 'rhel' || platform == 'debian' || (platform == 'ubuntu' && platform_version == '14.04')
352   # Note: docker_ver.empty? -> the latest version
353   if docker_ver.empty? \
354     || gem_ver(docker_ver) >= gem_ver('1.12')
355     extra_options = extra_options.gsub(%r{-H\sfd://}, '')  # for frozen string.
356   end
357 end
358 if docker_ver.empty? \
359   || gem_ver(docker_ver) > gem_ver('18.06.99')
360   extra_options = extra_options.gsub(%r{-H\sfd://}, '-H unix://')  # for frozen string.
361 end
362
363 docker_opts.push(extra_options) if !extra_options.nil? && !extra_options.empty?
364
365 if init_package == 'systemd'
366   directory override_dir_path do
367     owner 'root'
368     group 'root'
369     mode '0755'
370     action :create
371   end
372
373   template override_conf_path do
374     source  'etc/systemd/system/docker.service.d/override.conf'
375     owner 'root'
376     group 'root'
377     mode '0644'
378     variables(
379       docker_opts: docker_opts
380     )
381     not_if { install_flavor == 'os-repository' && platform_family == 'rhel' }
382     notifies :create, "directory[#{override_dir_path}]", :before
383     notifies :run, "bash[#{systemctl_daemon_reload}]", :immediately
384     notifies :restart, 'service[docker]'
385   end
386 elsif init_package == 'init'  # for Ubuntu 14.04,...
387   template '/etc/default/docker' do
388     source  'etc/default/docker'
389     owner 'root'
390     group 'root'
391     mode '0644'
392     variables(
393       docker_opts: docker_opts
394     )
395     notifies :restart, 'service[docker]'
396   end
397 end
398
399 service 'docker' do
400   provider Chef::Provider::Service::Upstart if platform == 'ubuntu' && platform_version < '15.04'
401   action [:start, :enable]
402   subscribes :restart, 'execute[update-ca-certificates]', :delayed
403 end
404
405 users = engine_conf['users_allow']
406 group 'docker' do
407   members users unless users.empty?
408   action :create
409   append true
410 end
411
412 # utility scripts
413 [
414   'docker_containers_cleanup',
415   'docker_images_cleanup',
416   'docker_volumes_cleanup',
417 ].each {|script|
418   template "/usr/local/bin/#{script}" do
419     source  "usr/local/bin/#{script}"
420     owner 'root'
421     group 'root'
422     mode '0755'
423     action :create
424   end
425 }
426
427 # autopilot
428 srv = 'dockerd'
429 template "/etc/cron.d/#{srv}-local" do
430   source  "etc/cron.d/#{srv}-local"
431   owner 'root'
432   group 'root'
433   mode '0644'
434   action :delete unless engine_conf['autopilot']['enabled']
435 end