Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Kubernetes CronJob Implementation in Detail #k8sjp
Search
Shimpei Otsubo
September 28, 2018
Programming
3.2k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Kubernetes CronJob Implementation in Detail #k8sjp
Shimpei Otsubo
September 28, 2018
More Decks by Shimpei Otsubo
See All by Shimpei Otsubo
Copy Kubernetes Clusters Really Fast
potsbo
3
5.3k
Go と Wantedly の関係 / How Wantedly uses Go
potsbo
1
940
Deploy Flow at Wantedly
potsbo
2
1.1k
Wrap every method with just one line
potsbo
1
5.7k
Zero yen Keyboard
potsbo
6
3.4k
Kube - The core tool at Wantedly
potsbo
1
8.5k
k8s - Kubernetes 8 Factors
potsbo
12
11k
コンテンツ作成に集中するためのプレゼンテーション Tips / Presentation with Confidence
potsbo
7
42k
ConfigMap vs Secret #k8sjp
potsbo
1
1.5k
Other Decks in Programming
See All in Programming
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
370
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
730
パーソナライズPodcastを作った話
repunit11
0
140
Are APIs Still Relevant in the AI Era?
soyuka
0
460
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
外から家のPCの電源入れたい!をPHPで解決する
kawashimo0
1
130
APNsからLive Activityを開始する話
yumnumm
0
170
dotNET_vulnclicker_intro.pdf
deka_morita
0
140
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
410
残高管理から台帳サービスへの進化
artoy
1
200
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
1
2.3k
Featured
See All Featured
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.9k
Context Engineering - Making Every Token Count
addyosmani
9
1.2k
How to build a perfect <img>
jonoalderson
1
6.1k
Six Lessons from altMBA
skipperchong
29
4.5k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.3k
Paper Plane
katiecoart
PRO
5
54k
Documentation Writing (for coders)
carmenintech
77
5.6k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
350
How to Think Like a Performance Engineer
csswizardry
28
2.9k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
5
810
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
320
Transcript
©2018 Wantedly, Inc. CronJob Implementation in Detail ίʔυ͔ΒಡΈղ͘ CronJob ͷৄࡉ༷
Kubernetes Meetup Tokyo #13 28.Sep.2018 - Shimpei Otsubo - @potsbo
CronJob ʹ·͞Ε͖ͯͨʜ ࣮ߦ࿙Ε ࣮ߦΕ ҙਤ͠ͳ͍࣮ߦ ਂ༻ͷॏ͍+PC͕னؒʹಈ͍ͯ*ODJEFOU &SSPSʹ$PNQMFUFʹ͍ͳ͍ʜ ఔฏؾͰΕΔ BQQMZͨ͠ॠؒʹಈ͍ͯ͠·͏ EFMFUFDSFBUFͩͱൃੜ͠ͳ͍
©2018 Wantedly, Inc.
ຊ൪Ϋϥελͷ$SPO+PCͷԆΛܭଌͯ͠Έͨ ඵ༨༟ඵฏؾͰΕΔ n = 475 $SPO+PCԆώετάϥϜ 0 30 60 Ԇ
ඵ <5 <10 <15 <20 <25 <30 <35 <40 <45 <50 <55 <60 <65 <70 <75 <80 ݸ ˞4UBSUJOH%FBEMJOF4FDPOET͕ͷͷ ©2018 Wantedly, Inc.
// Run the main goroutine responsible for watching and syncing
jobs. func (jm *CronJobController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() glog.Infof("Starting CronJob Manager") // Check things every 10 second. go wait.Until(jm.syncAll, 10*time.Second, stopCh) <-stopCh glog.Infof("Shutting down CronJob Manager") } $SPO+PC$POUSPMMFS͕ಈ͖࢝ΊΔGVOD ©2018 Wantedly, Inc.
// Run the main goroutine responsible for watching and syncing
jobs. func (jm *CronJobController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() glog.Infof("Starting CronJob Manager") // Check things every 10 second. go wait.Until(jm.syncAll, 10*time.Second, stopCh) <-stopCh glog.Infof("Shutting down CronJob Manager") } FWFSZTFDPOEͱॻ͍ͯ͋Δ͕࣮શ෦ॲཧඵεϦʔϓ $SPO+PC͕ଟ͍ͱ୯ҐͰΕΔ͜ͱ͋Γͦ͏ ˞ҰճͷTZOD"MMʹຊʹͦΜͳʹ͕͔͔͍࣌ؒͬͯΔͷ͔ະܭଌ ©2018 Wantedly, Inc.
func syncOne(sj *batchv1beta1.CronJob, js []batchv1.Job, now time.Time, jc jobControlInterface, sjc
sjControlInterface, pc podControlInterface, recorder record.EventRecorder) { // 50+ lines omitted times, err := getRecentUnmetScheduleTimes(*sj, now) if err != nil { return } // 10+ lines omitted scheduledTime := times[len(times)-1] tooLate := false if sj.Spec.StartingDeadlineSeconds != nil { tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now) } if tooLate { return } // 30 lines omitted jobReq, err := getJobFromTemplate(sj, scheduledTime) if err != nil { return } jobResp, err := jc.CreateJob(sj.Namespace, jobReq) if err != nil { return } // 20+ lines omitted return } ˞దٓ؆ུԽ
func syncOne(sj *batchv1beta1.CronJob, js []batchv1.Job, now time.Time, jc jobControlInterface, sjc
sjControlInterface, pc podControlInterface, recorder record.EventRecorder) { // 50+ lines omitted times, err := getRecentUnmetScheduleTimes(*sj, now) if err != nil { return } // 10+ lines omitted scheduledTime := times[len(times)-1] tooLate := false if sj.Spec.StartingDeadlineSeconds != nil { tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now) } if tooLate { return } // 30 lines omitted jobReq, err := getJobFromTemplate(sj, scheduledTime) if err != nil { return } jobResp, err := jc.CreateJob(sj.Namespace, jobReq) if err != nil { return } // 20+ lines omitted return } ະ࣮ߦ࣌ࠁҰཡΛऔಘ ˞దٓ؆ུԽ
func syncOne(sj *batchv1beta1.CronJob, js []batchv1.Job, now time.Time, jc jobControlInterface, sjc
sjControlInterface, pc podControlInterface, recorder record.EventRecorder) { // 50+ lines omitted times, err := getRecentUnmetScheduleTimes(*sj, now) if err != nil { return } // 10+ lines omitted scheduledTime := times[len(times)-1] tooLate := false if sj.Spec.StartingDeadlineSeconds != nil { tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now) } if tooLate { return } // 30 lines omitted jobReq, err := getJobFromTemplate(sj, scheduledTime) if err != nil { return } jobResp, err := jc.CreateJob(sj.Namespace, jobReq) if err != nil { return } // 20+ lines omitted return } ൃݟ͕͗ͨ͢ʁఆ ະ࣮ߦ࣌ࠁҰཡΛऔಘ ˞దٓ؆ུԽ
func syncOne(sj *batchv1beta1.CronJob, js []batchv1.Job, now time.Time, jc jobControlInterface, sjc
sjControlInterface, pc podControlInterface, recorder record.EventRecorder) { // 50+ lines omitted times, err := getRecentUnmetScheduleTimes(*sj, now) if err != nil { return } // 10+ lines omitted scheduledTime := times[len(times)-1] tooLate := false if sj.Spec.StartingDeadlineSeconds != nil { tooLate = scheduledTime.Add(time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)).Before(now) } if tooLate { return } // 30 lines omitted jobReq, err := getJobFromTemplate(sj, scheduledTime) if err != nil { return } jobResp, err := jc.CreateJob(sj.Namespace, jobReq) if err != nil { return } // 20+ lines omitted return } +PCൃߦ ൃݟ͕͗ͨ͢ʁఆ ະ࣮ߦ࣌ࠁҰཡΛऔಘ ˞దٓ؆ུԽ
func getRecentUnmetScheduleTimes(sj batchv1beta1.CronJob, now time.Time) ([]time.Time, error) { starts :=
[]time.Time{} sched, err := cron.ParseStandard(sj.Spec.Schedule) if err != nil { return starts, fmt.Errorf("Unparseable schedule: %s : %s", sj.Spec.Schedule, err) } var earliestTime time.Time if sj.Status.LastScheduleTime != nil { earliestTime = sj.Status.LastScheduleTime.Time } else { earliestTime = sj.ObjectMeta.CreationTimestamp.Time } if sj.Spec.StartingDeadlineSeconds != nil { // Controller is not going to schedule anything below this point schedulingDeadline := now.Add(-time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)) if schedulingDeadline.After(earliestTime) { earliestTime = schedulingDeadline } } if earliestTime.After(now) { return []time.Time{}, nil } for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) { starts = append(starts, t) } return starts, nil } ˞దٓ؆ུԽ
func getRecentUnmetScheduleTimes(sj batchv1beta1.CronJob, now time.Time) ([]time.Time, error) { starts :=
[]time.Time{} sched, err := cron.ParseStandard(sj.Spec.Schedule) if err != nil { return starts, fmt.Errorf("Unparseable schedule: %s : %s", sj.Spec.Schedule, err) } var earliestTime time.Time if sj.Status.LastScheduleTime != nil { earliestTime = sj.Status.LastScheduleTime.Time } else { earliestTime = sj.ObjectMeta.CreationTimestamp.Time } if sj.Spec.StartingDeadlineSeconds != nil { // Controller is not going to schedule anything below this point schedulingDeadline := now.Add(-time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)) if schedulingDeadline.After(earliestTime) { earliestTime = schedulingDeadline } } if earliestTime.After(now) { return []time.Time{}, nil } for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) { starts = append(starts, t) } return starts, nil } Ͳͷఔաڈ͔Β୳͔͢ʁ ˞దٓ؆ུԽ
func getRecentUnmetScheduleTimes(sj batchv1beta1.CronJob, now time.Time) ([]time.Time, error) { starts :=
[]time.Time{} sched, err := cron.ParseStandard(sj.Spec.Schedule) if err != nil { return starts, fmt.Errorf("Unparseable schedule: %s : %s", sj.Spec.Schedule, err) } var earliestTime time.Time if sj.Status.LastScheduleTime != nil { earliestTime = sj.Status.LastScheduleTime.Time } else { earliestTime = sj.ObjectMeta.CreationTimestamp.Time } if sj.Spec.StartingDeadlineSeconds != nil { // Controller is not going to schedule anything below this point schedulingDeadline := now.Add(-time.Second * time.Duration(*sj.Spec.StartingDeadlineSeconds)) if schedulingDeadline.After(earliestTime) { earliestTime = schedulingDeadline } } if earliestTime.After(now) { return []time.Time{}, nil } for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) { starts = append(starts, t) } return starts, nil } Ͳͷఔաڈ͔Β୳͔͢ʁ ͦͷաڈ͔Βࠓ·ͰϚον͢Δ࣌ࠁΛϦετ ˞దٓ؆ུԽ
$SPO+PC͔Β+PCൃߦ·ͰͷྲྀΕ ʮ࠷ޙͷ࣮ߦ࣌ࠁcc࡞࣌ࠁʯ͔Βࠓ·Ͱͷ࣌ࠁީิͷ࠷ޙ͕ StartingDeadlineSeconds ҎͳΒ࣮ߦ ©2018 Wantedly, Inc.
$SPO+PCͷBQQMZ࣌ʹ͍͖ͳΓKPC͕Γग़͢ ໌͔ΒAM 3:00 ʹಈ͍ͯ΄͍͠ 1:00 AM 3:00 AM ୳͢ൣғ 3:00
AM ະ࣮ߦఆ ?:?? ?? ҙਤ͠ͳ͍Job࣮ߦ ࠓͷAM 1:00 ͔Β apply ͷॠؒ·ͰʹࠓͷAM 3:00 ͕ೖͬͯ͠·͏ apply ͷޙʮະ࣮ߦͷ+PCͩʯͱೝࣝ͞Εͯͦͷॠؒಈ͖ग़͢ apply StartingDeadlineSeconds͕খ͚͞Ε͜͜ͰTLJQͯ͠Β͑Δ ࠓ ໌ ࡢ ࠷ޙ 1:00 AM ©2018 Wantedly, Inc.
$SPO+PCͷBQQMZ࣌ʹ͍͖ͳΓKPC͕Γग़͢ ໌͔ΒAM 3:00 ʹಈ͍ͯ΄͍͠ delete/createͩͱ࡞ΒΕͨ࡞ΒΕͨॠ͔ؒΒ୳࢝͠ΊΔͷͰ࣍ͷ".·Ͱ࣮ߦ͞Εͳ͍ ͨͩ͠Ұॠͱ͍͑ CronJob ͕ଘࡏ͠ͳ͍Մೳੑ͕͋ΔͷͰ·ͱΊͯΔͱ࣮ߦ࿙ΕϦεΫ 1:00 AM
3:00 AM delete/create ࠓ ໌ ࡢ 1:00 AM ©2018 Wantedly, Inc.
·ͱΊ ίʔυҙ֎ͱಡΊΔ CronJob ؆୯ʹΕΔ 4UBSUJOH%FBEMJOF4FDPOET৻ॏʹ ©2018 Wantedly, Inc.