diff options
| author | Franck Cuny <franck@fcuny.net> | 2025-09-29 17:48:39 -0700 |
|---|---|---|
| committer | Franck Cuny <franck@fcuny.net> | 2025-09-29 17:48:39 -0700 |
| commit | 070011b105dbf63369c5389115efccd079491aae (patch) | |
| tree | 34f72cbd1c25436fc8285a468a3c390bc5399cc0 /app/fcuny-net | |
| parent | add pr-analyzer (diff) | |
| download | x-070011b105dbf63369c5389115efccd079491aae.tar.gz | |
replacing my static website with a simple web app
Diffstat (limited to 'app/fcuny-net')
| -rw-r--r-- | app/fcuny-net/content/home.md | 8 | ||||
| -rw-r--r-- | app/fcuny-net/content/resume.md | 158 | ||||
| -rw-r--r-- | app/fcuny-net/main.go | 251 | ||||
| -rw-r--r-- | app/fcuny-net/static/main.css | 24 | ||||
| -rw-r--r-- | app/fcuny-net/static/resume.css | 189 | ||||
| -rw-r--r-- | app/fcuny-net/templates/home.html | 14 | ||||
| -rw-r--r-- | app/fcuny-net/templates/resume.html | 14 |
7 files changed, 658 insertions, 0 deletions
diff --git a/app/fcuny-net/content/home.md b/app/fcuny-net/content/home.md new file mode 100644 index 0000000..edc90f8 --- /dev/null +++ b/app/fcuny-net/content/home.md @@ -0,0 +1,8 @@ +👋 I'm Franck, and this is my little corner on the web. + +- I'm a Technical Director at [Roblox](https://www.roblox.com) +- Previously, I worked at [Twitter](https://twitter.com/TwitterEng) for close to 8 years. Before Twitter, I worked for different startups in S.F and Paris +- I focus on Compute Infrastructure and Reliability +- I live in Berkeley, and I'm married to the multi-talented artist [Felicia Martinez](https://www.feliciamartinez.com/about-1) + +The simplest way to contact me is via [email](mailto:hi@fcuny.net). diff --git a/app/fcuny-net/content/resume.md b/app/fcuny-net/content/resume.md new file mode 100644 index 0000000..eadcdb4 --- /dev/null +++ b/app/fcuny-net/content/resume.md @@ -0,0 +1,158 @@ +# Franck Cuny + +Technical Director Site Reliability Engineer + +Email: franck@fcuny.net | Phone: 415-617-5129 + +Results-driven Site Reliability Engineering leader with extensive +experience in architecting, scaling, and optimizing large-scale +distributed systems. Proven track record of driving reliability +improvements, fostering cross-functional collaboration, and mentoring +engineering talent. Dedicated to building resilient infrastructures and +cultivating a strong reliability culture. + +## Core Competencies: + +- Technical leadership and mentorship +- Cross-team collaboration and communication +- Large-scale distributed systems architecture +- Reliability engineering and disaster recovery +- Infrastructure optimization and cost reduction +- Production readiness and failure testing methodologies + +## Career Focus: + +Seeking opportunities to lead transformative reliability initiatives, +mentor the next generation of SREs, and drive architectural decisions +that significantly enhance system resilience and performance at scale. + +# Experience + +## Roblox, San Mateo + +______________________________________________________________________ + +Site Reliability Engineer Technical Director (IC7) August 2024 - to date +Site Reliability Engineer Principal II (IC6) Feb 2022 - August 2024 + +______________________________________________________________________ + +As a Team Lead for the Site Reliability group, I define road-maps, +milestones, and identify areas where SREs can partner with different +teams to improve overall reliability of our infrastructure and services. +Key projects and responsibilities include: + +- \*[Cell Architecture Implementation\* + ](https://corp.roblox.com/newsroom/2023/12/making-robloxs-infrastructure-efficient-resilient): + Led the SRE effort to transition from monolithic Compute clusters to + a Cell architecture, significantly enhancing Roblox's + infrastructure resilience and efficiency. Developed migration plans, + identified necessary automation, and drove production readiness for + this critical reliability improvement. + +- **Edge Infrastructure Migration**: Spearheaded the migration from + HAproxy to Envoy at the edge, aimed at reducing failure domains, + improving performance by streamlining the proxy chain, and enabling + user traffic steering to specific cells from the edge. + +- **Active/Passive Reliability Lead**: Orchestrated the failover + strategy across multiple teams, developing detailed action plans and + validation procedures. Conducted comprehensive tests to ensure plan + effectiveness. This work reduced the amount of time for a fail-over + from days to hours. + +- **Reliability Culture Champion**: Mentored engineers of various + levels (both SREs and SWEs), established a model for production + readiness, and popularized the practice of running failure exercises + for new large infrastructure projects. + +- **Technical Leadership**: Acted as tech lead on numerous projects, + demonstrating strong cross-team collaboration skills. Provided + technical guidance and mentorship to the SRE team, fostering a + culture of reliability and continuous improvement. + +Key strengths include driving complex infrastructure projects, +mentoring, setting reliability standards, and facilitating effective +cross-team collaboration. + +## Twitter, San Francisco + +______________________________________________________________________ + +Site Reliability Engineer Senior Staff Engineer Jan 2018 - Jan 2022 +Site Reliability Engineer Staff Engineer Aug 2016 - Jan 2018 +Site Reliability Engineer Senior Engineer Aug 2014 - Jan 2016 + +______________________________________________________________________ + +### Key Achievements and Responsibilities: + +- **Large-Scale Infrastructure Management**: Led SRE efforts for one + of the world's largest compute clusters (Mesos), spanning hundred + of thousands of nodes across multiple data centers. Defined KPIs and + improved automation for managing a massive fleet of bare metal + machines. + +- **Kubernetes Adoption**: Spearheaded the initiative to adopt + Kubernetes for on-premise infrastructure, driving architectural + decisions and implementation strategies. + +- **Cost Optimization**: Designed and implemented strategies that + significantly improved hardware utilization, resulting in tens of + millions of dollars in savings on hardware costs. + +- **Tech Leadership**: Served as Tech Lead for a team of 6 SREs + supporting Compute infrastructure. Established critical team + processes including on-call rotations and postmortem procedures. + +- **Cloud and On-Premise Expertise**: Led multiple efforts related to + Kubernetes deployment and management, both in cloud environments and + on-premise infrastructure. + +- **Storage Systems Migration**: Successfully migrated all pub-sub + systems from bare-metal deployment to Aurora/Mesos, pioneering the + adoption of the Compute orchestration platform among storage teams. + This transition reduced operational overhead, decreased deployment + times, and enhanced overall system reliability. + +- **Network Infrastructure Improvement**: Advocated for and + implemented the adoption of 10Gb+ networking in data centers, + enabling significant scaling improvements for storage systems. + +- **Cross-Functional Leadership**: Served as the SRE Tech Lead for the + real time storage team, driving improvements in performance, + operations, and automation across storage systems. + +I consistently demonstrated the ability to lead complex technical +initiatives, deliver impactful projects on-time, optimize large-scale +systems, and drive cross-functional collaboration to achieve significant +improvements in infrastructure reliability, efficiency, and +cost-effectiveness. + +## Say Media, San Francisco + +______________________________________________________________________ + +Software Engineer Senior Engineer Aug 2011 - Aug 2014 + +______________________________________________________________________ + +During my time at Say Media, I worked on two different teams. I started +as a software engineer in the platform team building APIs then I then +transitioned to the operation team to develop tooling in order to +increase the effectiveness of the engineering organization. + +## Linkfluence, Paris + +______________________________________________________________________ + +Software Engineer Senior SWE July 2007 - July 2011 + +______________________________________________________________________ + +I was one of the early engineers joining Linkfluence in 2007. I led the +development of the company's crawler (web, feeds). I was responsible +for defining the early architecture of the company, and designed the +internal platforms (Service Oriented Architecture). I contributed to +multiple open source projects on behalf of the company and represented +the company at numerous open source conferences in Europe. diff --git a/app/fcuny-net/main.go b/app/fcuny-net/main.go new file mode 100644 index 0000000..8976964 --- /dev/null +++ b/app/fcuny-net/main.go @@ -0,0 +1,251 @@ +package main + +import ( + "context" + "embed" + "fmt" + "html/template" + "io/fs" + "log" + "net/http" + "os" + "os/signal" + "path" + "runtime" + "syscall" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "rsc.io/markdown" +) + +//go:embed content/* +var contentFiles embed.FS + +//go:embed static/* +var staticFiles embed.FS + +//go:embed templates/* +var templateFiles embed.FS + +const ( + domain = "fcuny.net" + forgeDomain = "code.fcuny.net" + forgeUser = "fcuny" +) + +type PageData struct { + Title string + Content template.HTML +} + +var ( + goGetReqs = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "goget_requests_total", + Help: "go get requests processed, by repository name.", + }, []string{"name"}) + + staticReqs = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "static_requests_total", + Help: "HTTP requests served from the FS.", + }, []string{"path"}) + + modules = []string{"x"} +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + metricsMux := http.NewServeMux() + metricsMux.Handle("/metrics", promhttp.Handler()) + metricsMux.Handle("/.info", info()) + + metricsServer := &http.Server{ + Addr: ":9091", + Handler: metricsMux, + ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, + } + + go func() { + log.Println("Starting metrics server on :9091") + if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("Metrics server error: %v", err) + } + }() + + s := &http.Server{ + Addr: ":8070", + Handler: handler(), + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 1 * time.Minute, + } + + go func() { + log.Println("Starting main server on :8070") + if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("Main server error: %v", err) + } + }() + + <-ctx.Done() + log.Println("Shutdown signal received, starting graceful shutdown...") + + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer shutdownCancel() + + var shutdownErr error + + log.Println("Shutting down main server...") + if err := s.Shutdown(shutdownCtx); err != nil { + log.Printf("Main server shutdown error: %v", err) + shutdownErr = err + } + + log.Println("Shutting down metrics server...") + if err := metricsServer.Shutdown(shutdownCtx); err != nil { + log.Printf("Metrics server shutdown error: %v", err) + if shutdownErr == nil { + shutdownErr = err + } + } + + if shutdownErr != nil { + log.Printf("Shutdown completed with errors") + os.Exit(1) + } + + log.Println("Shutdown completed successfully") +} + +func handler() http.Handler { + mux := http.NewServeMux() + + staticFS, _ := fs.Sub(staticFiles, "static") + fileServer := http.FileServer(http.FS(staticFS)) + mux.Handle("/static/", http.StripPrefix("/static/", fileServer)) + + mux.HandleFunc("/", homeHandler) + mux.HandleFunc("/resume", resumeHandler) + + for _, name := range modules { + module := "/" + name + mux.Handle( + module+"/", + goImportHandler(module, "https://"+forgeDomain+"/"+forgeUser+"/"+name), + ) + } + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet && r.Method != http.MethodHead { + status := http.StatusMethodNotAllowed + http.Error(rw, http.StatusText(status), status) + return + } + mux.ServeHTTP(rw, r) + }) +} + +func homeHandler(w http.ResponseWriter, r *http.Request) { + markdownContent, err := contentFiles.ReadFile("content/home.md") + if err != nil { + log.Printf("Error reading home markdown: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + parser := markdown.Parser{} + doc := parser.Parse(string(markdownContent)) + + htmlContent := markdown.ToHTML(doc) + + tmplContent, err := templateFiles.ReadFile("templates/home.html") + if err != nil { + log.Printf("Error reading template: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + tmpl, err := template.New("layout").Parse(string(tmplContent)) + if err != nil { + log.Printf("Error parsing template: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + data := PageData{ + Title: "Franck Cuny", + Content: template.HTML(htmlContent), + } + + w.Header().Set("Content-Type", "text/html; charset=UTF-8") + if err := tmpl.Execute(w, data); err != nil { + log.Printf("Error executing template: %v", err) + } + staticReqs.WithLabelValues(path.Clean(r.URL.Path)).Inc() +} + +func resumeHandler(w http.ResponseWriter, r *http.Request) { + markdownContent, err := contentFiles.ReadFile("content/resume.md") + if err != nil { + log.Printf("Error reading resume markdown: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + parser := markdown.Parser{} + doc := parser.Parse(string(markdownContent)) + + htmlContent := markdown.ToHTML(doc) + + tmplContent, err := templateFiles.ReadFile("templates/resume.html") + if err != nil { + log.Printf("Error reading template: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + tmpl, err := template.New("layout").Parse(string(tmplContent)) + if err != nil { + log.Printf("Error parsing template: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + data := PageData{ + Title: "Work Experience", + Content: template.HTML(htmlContent), + } + + w.Header().Set("Content-Type", "text/html; charset=UTF-8") + if err := tmpl.Execute(w, data); err != nil { + log.Printf("Error executing template: %v", err) + } + staticReqs.WithLabelValues(path.Clean(r.URL.Path)).Inc() +} + +func goImportHandler(module, repo string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + goGetReqs.WithLabelValues(module).Inc() + + if r.URL.Query().Get("go-get") == "1" { + w.Header().Set("Content-Type", "text/html; charset=UTF-8") + importPath := domain + module + if _, err := fmt.Fprintf(w, `<html><head><meta name="go-import" content="%s git %s"></head></html>`, importPath, repo); err != nil { + log.Printf("Error writing response: %v", err) + } + return + } + + http.Redirect(w, r, repo, http.StatusFound) + }) +} + +func info() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Go version: %s\n", runtime.Version()) + }) +} diff --git a/app/fcuny-net/static/main.css b/app/fcuny-net/static/main.css new file mode 100644 index 0000000..12df426 --- /dev/null +++ b/app/fcuny-net/static/main.css @@ -0,0 +1,24 @@ +body { + width: auto; + max-width: 700px; + padding: 0 15px; + margin: 5rem auto; + font: + 1rem/2 -apple-system, + sans-serif; + background-color: white; + color: #242936; +} +p { + margin: 0 0 1em; +} +a { + color: inherit; + text-decoration: none; + border-bottom: 2px solid #007f9f; +} +ul { + display: inline-block; + text-align: left; + padding-left: 1em; +} diff --git a/app/fcuny-net/static/resume.css b/app/fcuny-net/static/resume.css new file mode 100644 index 0000000..3fa95f0 --- /dev/null +++ b/app/fcuny-net/static/resume.css @@ -0,0 +1,189 @@ +/* Reset and base styles */ +* { + box-sizing: border-box; +} + +body { + font-family: -apple-system, system-ui, sans-serif; + font-size: 16px; + line-height: 1.6; + color: #2c3e50; + margin: 0 auto; + padding: 2rem 1rem; + max-width: 50rem; + background: #fff; +} + +/* Typography hierarchy */ +h1 { + font-size: 1.75rem; + font-weight: 700; + color: #2c3e50; + margin: 0 0 0.5rem 0; + line-height: 1.2; +} + +h2 { + font-size: 1.25rem; + font-weight: 600; + color: #34495e; + margin: 2rem 0 1rem 0; + padding-bottom: 0.5rem; + border-bottom: 2px solid #e9ecef; +} + +h3 { + font-size: 1.1rem; + font-weight: 600; + color: #495057; + margin: 1.5rem 0 0.75rem 0; +} + +/* Header section */ +#title-block-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 3px solid #3498db; +} + +/* Contact info and summary */ +.contact-info { + font-size: 0.95rem; + color: #6c757d; + margin-bottom: 1.5rem; +} + +.contact-info a { + color: #3498db; + text-decoration: none; +} + +.contact-info a:hover { + text-decoration: underline; +} + +/* Lists */ +ul { + margin: 1rem 0; + padding-left: 1.5rem; +} + +li { + margin-bottom: 0.5rem; +} + +/* Job tables */ +table { + width: 100%; + margin: 1rem 0; + border-collapse: collapse; +} + +td { + padding: 0.25rem 1rem 0.25rem 0; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; + font-size: 0.9rem; + vertical-align: top; +} + +td:last-child { + text-align: right; + font-weight: 500; + color: #6c757d; +} + +td:first-child { + font-weight: 600; + color: #495057; +} + +td:nth-child(2) { + color: #6c757d; +} + +/* Links */ +a { + color: #3498db; + text-decoration: none; + transition: color 0.2s ease; +} + +a:hover { + color: #2980b9; + text-decoration: underline; +} + +/* Strong text for emphasis */ +strong { + font-weight: 600; + color: #2c3e50; +} + +/* Improved spacing for sections */ +#experience { + margin-top: 2rem; +} + +/* Company sections */ +h2[id*="roblox"], +h2[id*="twitter"], +h2[id*="say-media"], +h2[id*="linkfluence"] { + margin-top: 2.5rem; + font-size: 1.3rem; + color: #2c3e50; +} + +/* Better paragraph spacing */ +p { + margin: 0.75rem 0; +} + +/* Print styles */ +@media print { + body { + font-size: 12px; + line-height: 1.4; + padding: 0; + } + + h1 { + font-size: 1.4rem; + } + + h2 { + font-size: 1.1rem; + page-break-after: avoid; + } + + table { + page-break-inside: avoid; + } + + a { + color: inherit; + text-decoration: none; + } +} + +/* Mobile responsiveness */ +@media (max-width: 600px) { + body { + padding: 1rem 0.75rem; + font-size: 15px; + } + + h1 { + font-size: 1.5rem; + } + + table { + font-size: 0.85rem; + } + + td:last-child { + text-align: left; + font-size: 0.8rem; + } +} diff --git a/app/fcuny-net/templates/home.html b/app/fcuny-net/templates/home.html new file mode 100644 index 0000000..d796401 --- /dev/null +++ b/app/fcuny-net/templates/home.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{.Title}}</title> + <link rel="stylesheet" href="/static/main.css"> +</head> +<body> + <main> + {{.Content}} + </main> +</body> +</html> diff --git a/app/fcuny-net/templates/resume.html b/app/fcuny-net/templates/resume.html new file mode 100644 index 0000000..86e854b --- /dev/null +++ b/app/fcuny-net/templates/resume.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{.Title}}</title> + <link rel="stylesheet" href="/static/resume.css"> +</head> +<body> + <main> + {{.Content}} + </main> +</body> +</html> |
