Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
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
930
Deploy Flow at Wantedly
potsbo
2
1.1k
Wrap every method with just one line
potsbo
1
5.6k
Zero yen Keyboard
potsbo
6
3.4k
Kube - The core tool at Wantedly
potsbo
1
8.4k
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
Gmail/Google DriveをトリガーにAIエージェントを動かそう! / Run AI agents with Gmail/Google Drive as triggers!
har1101
3
460
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
5
1.8k
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
180
VibeCodingからAgenticWorkflowへ
starfish719
0
940
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
2
830
Hello, Hiroshima Geospatial Data! — Exploring DoboX with Python
ra0kley
0
110
Go を使い始めて 2 ヶ月の学び / My first two months with Go
contour_gara
0
390
ALB ログから Trace を気合で繋げる技術
fohte
7
770
初めての模倣学習とVLA
natsutan
0
320
業務時間外もAIに働いてもらう話
colorful12
2
8.7k
承認済みなのに差戻しできてしまうバグ、型で潰せます
shinchit
0
120
ドリフトを絶対に許さない(?)CDK運用 / CDK Ops with Zero Tolerance for Drifts (?)
akihisaikeda
1
240
Featured
See All Featured
WCS-LA-2024
lcolladotor
0
810
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Making the Leap to Tech Lead
cromwellryan
135
10k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
A Soul's Torment
seathinner
6
3.5k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Practical Orchestrator
shlominoach
191
12k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
Faster Mobile Websites
deanohume
310
32k
Tell your own story through comics
letsgokoyo
1
1k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
390
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.