www.benguild ベン・ギルド: Somewhere in the digital forest, making apps, video, hardware, and photographs. https://benguild.com Thu, 01 Jan 2026 00:23:31 -0800 Thu, 01 Jan 2026 00:23:31 -0800 1800 https://benguild.com/favicon-32x32.png www.benguild https://benguild.com 32 32 Migrating Windows from Intel (x86) VirtualBox to ARM Windows on M1 Mac computers Before I switched to a 2021 MacBook Pro with the M1 Max (Apple silicon using the ARM architecture), I was on an older Intel-based (x86) MacBook Pro like everyone else using otherwise recent Mac hardware. One of the pieces of software I used on my machine rarely but intentionally was VirtualBox, a virtualization product that can be used to run additional operating systems on your Intel-based Mac (or other x86 machine) such as Microsoft Windows.

However, since VirtualBox is a hypervisor, I couldn't simply run my x86 Virtual Machines this way anymore. The ARM architecture of Apple silicon couldn't simply be “partitioned” or shared with a virtualized software system that was expecting access to real x86 hardware.

I researched this situation a bit, and found that UTM was a great way to run an ARM-supported version of Windows, but I still had the hope to access my prior Windows x86 installation from VirtualBox so that I could grab any files and software configurations that I still needed from there.

Getting UTM + virtualizing “Windows on ARM”

UTM (which is powered by QEMU) is a free download (although it costs something if you get it from the Mac App Store instead of the website directly), and supports a lot more than just virtualizing macOS instances or emulating x86 architecture. It'll even let you boot older PowerPC builds of Mac OS 9, for example.

A screenshot of the UTM app for Mac kickstarting a new virtual machine, and the choice to “Virtualize” or ”Emulate” depending on the host and virtual machine's hardware/software intercompatibility.
A screenshot of the UTM app for Mac kickstarting a new virtual machine, and the choice to “Virtualize” or ”Emulate” depending on the host and virtual machine's hardware/software intercompatibility.

Windows on ARM” is fast on UTM with the M1 Max.  …It's a delight to use, and not too difficult to set up either.  🐇

Booting VirtualBox x86 Virtual Disk Images

UTM will also boot and emulate x86 virtual machines on Apple silicon, not just those running ARM-targeted operating systems. However, a more modern, demanding x86 operating system may have significantly reduced performance in comparison to native M1 performance.

Luckily, it may still be performant and stable enough for use in migrating files and settings to your new ARM-based Windows installation, for example, which can afterward run a lot of your x86-compiled Windows software instead.

Steps I used:

  1. Create an additional new virtual machine in UTM (don't use your ARM Windows virtual machine for this), and choose to “Emulate” (rather than “Virtualize”) and then select the “Other” type for the system. (…not the “Windows” one!)
  2. Select the “Skip ISO boot” option on the following screen. This will bypass the expectation that you'll install a new operating system.
  3. Once created, edit the Virtual Machine before booting it. Import your previous VirtualBox *.VDI disk image file as a new drive, which should automatically clone and convert it to the required format within UTM/QEMU without modifying the original source image.
  4. You may also need to uncheck the “UEFI Boot” option under “QEMU” settings for this virtual machine in order for it to start Windows.

… Hopefully, after starting and an automatic subsequent reboot by Windows (after detecting the environment's massive hardware configuration change from when it ran under VirtualBox), you should see your Windows Desktop once again and then be able to migrate data via your host system to its new ARM-targeted virtual machine counterpart!

After migrating and verifying everything, I personally just shut down and deleted my x86 Windows virtual machine, because the ARM version runs so much faster natively by comparison.  🐌

]]>
https://benguild.com/2022/07/02/virtualbox-to-m1-mac-utm-arm-qemu/ https://benguild.com-/2022/07/02/virtualbox-to-m1-mac-utm-arm-qemu Sat, 02 Jul 2022 21:17:55 -0700
Regarding perception and dreaming… Excerpt from The Four Agreements by Don Miguel Ruiz:

What you are seeing and hearing right now is nothing but a dream. You are dreaming right now in this moment. You are dreaming with the brain awake.

Dreaming is the main function of the mind, and the mind dreams twenty-four hours a day. It dreams when the brain is awake, and it also dreams when the brain is asleep. The difference is that when the brain is awake, there is a material frame that makes us perceive things in a linear way. When we go to sleep we do not have the frame, and the dream has the tendency to change constantly.

Humans are dreaming all the time. Before we were born the humans before us created a big outside dream that we will call society's dream or the dream of the planet. The dream of the planet is the collective dream of billions of smaller, personal dreams, which together create a dream of a family, a dream of a community, a dream of a city, a dream of a country, and finally a dream of the whole humanity.

]]>
https://benguild.com/2021/02/27/dreams/ https://benguild.com-/2021/02/27/dreams Sat, 27 Feb 2021 21:32:25 -0800
Return anything: Amazon and eBay turn a blind-eye to “Missing Parts” or “Defective” merchandise Since the dot-com boom, internet companies have driven a shift away from brick-and-mortar businesses where actual, real people would inspect returns before accepting them. I just wanted to do a quick post about the lingering return fraud in today's e-commerce industry.

By default, these platforms are so large and handle so much transaction volume that they tend to shift this return handling liability over to third-party sellers, since combating return fraud does not scale at a large internet business where the transaction volume per human worker is so great. Third-party sellers have little maneuverability here, as having a zero-friction resolution process would ensure that said process would be taken advantage of by dishonest buyers, dishonest sellers, or both… introducing a significant financial liability for these large-scale “middlemen on commission.”

But, as middlemen, these platforms have to intervene when something goes wrong. Platform success is fueled primarily by honest consumers buying product, but complaints tend to be honored without recourse, even if they are invalid or fraudulent. — This Reddit post from 2019 (and the emotional responses from smaller sellers in its comments section) highlights the fact:

ULPT: Any eBay purchase can be returned without penalty (including full refund of shipping both ways) by just saying it's “missing parts.” — eBay doesn't handle goods, so they will always side with the buyer. Zero exceptions. It's fully automated, and the seller has no recourse even by phone.

… The major issue is that these problematic transactions tend to be infrequent or so small that they are difficult to address and handle effectively, fairly, and consistently. Platforms are fighting for customers and to retain marketshare, but they have to find a way to bear these costs and keep buyers happy, or they will have no sellers at all due to lack of buyer traffic. By shifting these troublesome transactions onto third-party sellers and treating them as liable by default, they've found a way to defer dealing with something very difficult and unappealing at scale, especially if the seller is given no recourse.

For larger sellers, return fraud is something they accept as an expense as a cost of participating in these platforms. Some buyers eventually get caught. For smaller sellers who just sell occasionally, it can really sting, especially on large ticket items or infrequent sales. The loss of the brick-and-mortar relationship and the ability to say “no” has left the door open to this faceless behavior, all to handle more transactions with less and less human involvement.

]]>
https://benguild.com/2021/01/16/return-anything/ https://benguild.com-/2021/01/16/return-anything Sat, 16 Jan 2021 15:31:23 -0800
The first ski/snowboarding goggles I've ever liked Back when I lived in Tokyo, I used to hop on the high-speed “shinkansen (新幹線)” train to the mountains whenever it was forecasted to be a sunny, clear day… and ideally if there was some fresh snow scheduled to fall the night before. It was paradise, and the JR SKISKI train and lift ticket packages made it very affordable and convenient for both residents and foreigners.

However, this year, with COVID-19 still widely limiting travel worldwide, it was becoming a bit unclear how to best enjoy the winter season and participate in ski/snowboarding activities while working remotely.

Remote Work → Jackson Hole, WY 🏂

Given that many tech companies are offering some flexible work arrangements during the pandemic, three of us ended up renting a house in Jackson Hole for the season with a focus on isolating in a remote area.

A view from Jackson Hole Mountain Resort.
A view from Jackson Hole Mountain Resort.

We're up at a high altitude and experiencing all kinds of terrain and weather when skiing/snowboarding a few times a week. With this variability being the opposite of the sunny, clear, consistent days I'd targeted in past years in Japan, I knew the time had come to own real ski goggles for the first time in a long time.

The Smith ski goggles, beside my “all-weather” Smith sunglasses.
The Smith ski goggles, beside my “all-weather” Smith sunglasses.

What was I using before? Sunglasses. Why? Because in the past, I've absolutely hated ski goggles. They'd fog up, they can be super bulky, they limit your vision and get scratched… you name it. My experience with them had generally been awful.

The latest goggles from Smith

I swung by REI before I left California and spoke to a few members of the team regarding my frustrations overall with ski goggles. Together, we looked at a few newer, high-end pairs.

Close-up of the lens and model.
Close-up of the lens and model.

The ones I ended up deciding on were the “Smith 4D MAG ChromaPop Snow Goggles” and so far they've been fantastic.

Visibility + Light/Color

One of the advantages of the 4D MAG is their reduced limitation of your peripheral vision. This is noticeably better than on their other models, and on models from other manufacturers as well.

In terms of color and light, I have zero complaints about the two interchangeable lenses it came with, as they offer really fantastic light transmission without feeling too “amber” or disorienting. In terms of field of view, I can look down or to the side without feeling like the goggles' edges are in the way, which is perfect.

Anti-Fog + Fit

Part of what causes goggles to fog is poor fit or seal, as this can block vents that would otherwise de-fog the lens or can trigger improper airflow due to a limited seal. Zero complaints with the 4D MAG, even while wearing a facemask.

They also offer a “low-bridge” fit, in case that fits your face better!

Conclusion

I've worn Smith sunglasses for years and these goggles are great. They're not the cheapest pair in their collection (in fact, they're currently the most expensive) yet I was luckily able to pick up a pair of the same specification at a discount because they were from the prior model year. The frames had changed slightly between the two generations, but the lenses did not.

I unfortunately accidentally scratched one of the interchangeable lenses this past week while wiping out some ice that had built up inside, so my one word of caution with these and others is to avoid wearing them again until any ice has melted away. ⚠️

]]>
https://benguild.com/2020/12/26/first-ski-goggles-liked/ https://benguild.com-/2020/12/26/first-ski-goggles-liked Sat, 26 Dec 2020 23:17:09 -0800
“Shh…” launched UPDATE (2020/02/15): I'd initially released this project when it was only about two-thirds or so finished, but ultimately decided to shut it down due to a recent change in location and employment. — More information about the project can be found on my portfolio.

I've been tinkering with an app idea in some of my free time this year, and wanted to release it before 2020. It's an end-to-end encrypted, anonymized publisher/messenger… sort of like a cross between Signal and Twitter.

The branding for “Shh…”
The branding for “Shh…”

With it, you simply add one or more close friends to join a “network,” and then the devices will automatically exchange and maintain unique encryption keys to help keep your communications a secret from any possible eavesdroppers. 😃

It's a lightweight yet powerful concept. A few features are still missing that I'd hoped to include, but they'll perhaps show up sometime down the road.

Messages from you and from others are routed to your friends and beyond, until they eventually “die out” in one of a number of different ways. You won't know who wrote what, but you can block authors (anonymously) or optionally “hide” any message… essentially removing it from your networks if your friends haven't already received it from you or somebody else. — Authors and readers alike will “level up” by participating, so it's a bit “gamified.” 🎮

The app's still very “V1,” but if you're interested in checking it out, it's currently on the iOS App Store for free. (🇺🇸 🇬🇧 🇨🇦 🇯🇵 Available in the US, UK, Canada, and Japan initially.)

And, yeah… it's just pronounced like the “🤫” emoji.   🤷🏼‍♂️📲

]]>
https://benguild.com/2019/12/18/shh-launched/ https://benguild.com-/2019/12/18/shh-launched Wed, 18 Dec 2019 21:23:42 -0800
2.9-Inch ePaper Weather Display (Arduino powered/programmable) review and guide I recently relocated to Manhattan, and one of the things that I really wanted next to my apartment's front door was an ePaper weather display with a three-hour segment forecast for the coming day. ☀️ — For those unfamiliar, “electronic paper” (ePaper) is a display technology commonly seen in the Amazon Kindle that mimics ink on actual paper.

“ePaper” uses very little power, and does not have a distracting backlight that might otherwise light up a dark apartment at night, so I figured that this might be the perfect fit for this project. At first, I actually considered repurposing an old Kindle for it, but ultimately decided against that for practicality's sake.

After looking around a bit, I found the ThingPulse 2.9" ESPaper Lite Kit, ESP8266 WiFi ePaper display which is programmable with the Arduino IDE. — You'll probably also need:

The ePaper board arrived pretty quickly, but the serial adapter that I ended up ordering was coming from China, so that took about a week more. — However, setup was easy since I was already familiar with the Arduino IDE!

Downloading the software using my Mac's Arduino IDE installation, and living on the edge by using gravity instead of solder to touch the programmer's pins to those of the main board.
Transferring the software using my Mac's Arduino IDE installation, and living on the edge by using gravity instead of solder to touch the programmer's pins to those of the main board.

… When it had finished downloading, it instantly booted and just worked. 🆒

So, I put it by my door (within range of my Wi-Fi) and connected some USB power to ensure that it'd continue to run standalone, indefinitely:

The configured board with connected Wi-Fi and USB power.
The configured board with connected Wi-Fi and USB power.

Done! The two requests that I've put in to the software's developers are:

  • Vertical display rotation. I chose to mount the board upside down so that the power cord was oriented to the left, which will require a software UI tweak to reorient the button text shown on the screen to match the actual corresponding hardware buttons. #24
  • Support for hiding the “Wi-Fi failure” screen and using some sort of other visual feedback instead. Occasionally, I've found the board temporarily won't reconnect to my Wi-Fi. You can manually reset it using one of the buttons on it (and it seems to retry on its own periodically), but I'd honestly prefer to only show the error after several repeat failures in order to not block the full weather report unless it's become seriously stale. #25

… I might contribute some code to this repository in order to tweak these behaviors with new configuration options, although, because I personally haven't touched Arduino in a couple of years, it might take me a while to get around to that.

There are other great projects for signs/lighting for your home, too, such as the SF Muni LED Sign at Home with Raspberry Pi, so feel free to get creative. — This ePaper display in particular can run other software that you write for it, so if you're into that, you could use it for a calendar or something else cool, too. 👍🏻

]]>
https://benguild.com/2019/06/16/epaper-weather-display-arduino/ https://benguild.com-/2019/06/16/epaper-weather-display-arduino Sun, 16 Jun 2019 13:46:13 -0700
Received a cool shoutout from the Apple Security and Safari teams this week 🔐 The iOS 12.3 update launched yesterday.

A screenshot of Apple's article regarding the security content in yesterday's iOS 12.3 release.
A screenshot of the “About the security content of iOS 12.3” article on Apple's site.

… Thanks to both teams for the mention and their work on the patch! 🙌🏻

]]>
https://benguild.com/2019/05/14/apple-security-team-shoutout/ https://benguild.com-/2019/05/14/apple-security-team-shoutout Tue, 14 May 2019 09:39:08 -0700
Set a custom, emoji-filled lock screen message on your new iPhone or iPad! (“how-to” tutorial) When I first fully switched to iPhone back in 2010, one of the features I immediately missed most from my BlackBerry was the ability to set a lock screen message for the device.

Essentially this lets you print a sentence or two of text that appears after you turn it on but before you unlock it, such as your name and an alternate phone number, in case you lose the device or if it gets mixed up with someone else's, as shown below:

Demo of the secret lock screen ability on iOS.
Demo of the secret lock screen ability on iOS.

… Well, there's a hidden way to have this on iOS, too! 😃

Requirements and past alternatives

Apple makes doing this fairly cumbersome on a personal device unless you're willing to erase it beforehand. My assumption is that this is probably to avoid upsetting the iPhone's simple visual design language… which certainly doesn't consist of random emoji-ridden text on the otherwise elegant default wallpapers that ship with each device. 😅

Before this was even possible on a system level, you'd previously had to actually embed the text yourself into your device's wallpaper (which of course you are free to customize), or just omit it altogether unless using Find My iPhone to display a message temporarily if the phone is remotely disabled.

So, how do you do it? With Apple Configurator! …And, does it support emoji? Of course! (Which is good, because there's limited horizontal space… depending on which device you have.) — The caveat, though, is that a lock screen message can only be first added to a device that hasn't been setup yet (or that you can safely erase), and you must have a Mac that's running a fairly recent version of macOS in order to install the latest Apple Configurator version from the Mac App Store.

Walkthrough

Warning: Proceed at your own risk. You'll need to have a new or freshly-erased iOS device that has not gone past the “Hello” screen that appears on first boot! — Yes, it is possible to restore from a backup, but once you have left this screen on the device, you'll need to erase the device again in order to proceed with this walkthrough.

On Black Friday this year, I was able to grab an 11-inch iPad Pro (2018) for a discount with the help of a friend. Glowing about the potential of being able to replace all of the paper notebooks and blah-blah pens in my work bag with a digital surface and an inkless drawing device, I'd checked out the tablet the day prior at an Apple Store, and ended up going for it. While my keyboard case still hasn't arrived yet (it's out of stock locally 😢), I still ended up writing this blog post on it without much trouble!️

Step 1: Power up, but pause at “Hello” and go no further!

To start, turn on your new or freshly-erased iOS device if it's powered off.

If you don't see the “Hello” screen that first appears when setting a device up from scratch, or if you've previously moved past this screen, you'll need to first backup your device and erase it. Proceed with caution and at your own risk.

My 2018 iPad Pro, at the “Hello” screen. (shown here displaying「こんにちは!」in Japanese)
My 2018 iPad Pro, at the “Hello” screen. (shown here displaying「こんにちは」in Japanese)

Step 2: Connect to Mac via USB, and restore data from a backup if necessary.

Once you're at the “Hello” setup screen of the iPhone or iPad, open Apple Configurator on your Mac with the device attached via USB.

If you need to restore a backup using iTunes or Apple Configurator, do this from your Mac before proceeding to the next step and before moving past the “Hello” screen. — You'll probably want to quit Apple Configurator and use iTunes to restore the backup instead, because, at this time of writing, Apple Configurator will not restore your apps… but iTunes will.

You can quit iTunes when the restore completes and the phone is automatically rebooting, and when you restart iTunes again later, all previously synced content will begin to transfer automatically! 👍🏻

An iPad, ready for configuration in Apple Configurator.
The iPad, ready for configuration in Apple Configurator.

Step 3: “Prepare” and “supervise” the device using Apple Configurator

Tap the button to “Prepare” the device in the toolbar at the top of Apple Configurator on your Mac, and follow the steps illustrated below to “supervise” it.

Supervising your iOS device (1) Supervising your iOS device (2) Supervising your iOS device (3) Supervising your iOS device (4)

Step 4: Create your “lock screen message” profile

Once supervised, in the “File” menu of Apple Configurator, select the “New Profile” option, and then give the profile a name and unique identifier:

General iOS profile configuration settings in Apple Configurator.
General iOS profile configuration settings in Apple Configurator.

Afterward, edit the “Lock Screen Message” section as you please:

Lock screen message settings in Apple Configurator.
Lock screen message settings in Apple Configurator.

… Finally, save the profile somewhere handy on your computer. 💾

Step 5: Install the profile to your supervised device

When you've finished creating the profile, just add it to your supervised iOS device from the main screen of Apple Configurator:

Popover menu for adding a profile to a connected iOS device in Apple Configurator.
Popover menu for adding a profile to a connected iOS device in Apple Configurator.

You'll be able to add or remove any lock screen profile in the future on this device without having to erase it (since it's already supervised), in case you want to change the message later on. 👍🏻

Displaying an installed profile in Apple Configurator.
Displaying an installed profile in Apple Configurator.

… And, that's it! Enjoy being one of the few with a native lock screen message on iOS. 😄

]]>
https://benguild.com/2018/12/02/iphone-ipad-ios-lock-screen-message-configurator/ https://benguild.com-/2018/12/02/iphone-ipad-ios-lock-screen-message-configurator Sun, 02 Dec 2018 18:18:58 -0800
Quickstart to a load-balanced HTTP/2 web-app with Google “K8s” Kubernetes Engine, Golang (Go), and TLS by Let's Encrypt. (Bonus: gRPC, too!) Hello World.” That's what you'll be seeing soon from your Kubernetes cluster if you follow this tutorial! 💬 — Or, alternatively, an empty “SayHello” request and response via gRPC… a modern and preferable alternative to RESTful APIs. (with JSON, etc.)

To expose gRPC on Google Kubernetes Engine via Ingress isn't currently straightforward.
To expose gRPC on Google Kubernetes Engine via Ingress isn't currently straightforward.

Why not Google App Engine… or Cloud Functions?

I'm a big fan of Google App Engine (GAE) due to its “serverless” infrastructure and its ability to autoscale at a moment's notice to support huge surges of traffic… and, at a reasonable cost.

GAE even has built-in autoscaling Memcached and Datastore, and supports Let's Encrypt for no-cost, auto-renewing HTTPS/TLS support, as well. I've launched well-designed applications to huge sudden traffic spikes from sites like Hacker News and Product Hunt without issue, and Snapchat even used GAE to scale its way to an IPO. 📈 — But, it doesn't support HTTP/2 through to the application at this time of writing! 😞 GAE is unfortunately a bit dated at this point, and based on its design it's unclear whether it will ever fully support HTTP/2.

By nature, HTTP/2 offers bidirectional flow of data, yet the load balancer on GAE converts all HTTP/2 traffic to unidirectional HTTP/1.1 requests. Although HTTP/1.x still dominates traditional web traffic today, modern apps and browsers are quickly adopting HTTP/2. — Also unfortunate is that Cloud Functions, a potential successor to GAE, shares a lot of GAE's underlying infrastructure currently, and therefore also does not fully support HTTP/2. — So, for this project, we're “rolling our own” solution instead! 🤷🏼‍♂️

Comparison of levels of control between Kubernetes Engine, App Engine, and Compute Engine on Google Cloud Platform.
Comparison of levels of control between Kubernetes Engine (GKE), App Engine (GAE), and Compute Engine (GCE) on Google Cloud Platform. (image credit)

A well-configured Kubernetes setup can autoscale, although it has a higher upfront monthly cost and is less simple to configure and deploy. — If you go this route, hopefully this “quickstart” can help save you some time!

The codebase I'm providing to help you

There's some scaffolding you'll need to get started. I've created a repository on GitHub that includes:

  • A gRPC server, and a separate plain HTTP/2 server for a “health check.” (If you only want HTTP/2 not gRPC, just remove the gRPC dependencies and protos, and promote the health check server from its Go routine to replace the gRPC server that runs primarily!) 👍🏻
  • Code that generates self-signed certificates (for internal use) based on the node's IP address on launch. — Ingress will provide the client-facing TLS with a different certificate!
  • A template “proto” and “service” for gRPC, with an empty request and response. (For testing and verification from a client, but can be discarded if you're not using gRPC.)
  • Template deployable manifest files for the necessary additional configuration of Ingress on Google Cloud Platform. (For traffic and load balancing.)

… This will save you some steps and trouble. 😉

You'll need to edit each file to adjust your project name, prefix, and probably write some scripts to help you deploy more conveniently than running commands manually each time, but by the end of the quickstart, you'll hopefully have something running that you can iterate on. 📝

Please feel free to provide comments or feedback below or to me directly that might help others, and I'll update this post accordingly if necessary!

Let's deploy Kubernetes!

First of all, clone the code repository to your local `GOPATH` as a starting point: https://github.com/benguild/gke-grpc-example (… I'm assuming that you have a local Golang environment setup already on your computer!) 😅

Then, create a project on Google Cloud Console if you don't have one already to work with.

Create a Kubernetes cluster

For this step, visit the “Clusters” tab of Kubernetes Engine on Google Cloud Console, and tap the “Create cluster” button. — You can also do this from the command-line if you want.

There are a lot of settings you can customize here, but since this is a “quickstart” and you'll probably need to customize these later for your application based on its specific needs, I'm going to suggest just the minimum specs here:

  • Choose the name “example-grpc” for your cluster to ease deployment. (Once you're beyond the “Hello World” stage, you can refine the configuration and replace the cluster on your own.)
  • Be sure to choose at least version “1.10.7-gke.6” or later. (This will not work on older versions, as they do not support the “HTTP2” tag used in our configuration.)
  • If you choose a zone aside from the default “us-central1-a” zone, you'll need to update your configuration and commands below to that zone instead, so please keep this in mind.
  • Ensure that the “Enable HTTP load balancing” option is checked.
  • If you tap the “Advanced Edit” button for your “Node pool” that's created by default, you can tweak the settings there based on what I've outlined in the screenshot below. (To minimize costs to start, choose the “small” size for “Machine type” unless you're sure that your needs will exceed this right away!) 💸

    An suggested introductory “node pool” configuation for a Kubernetes cluster.

  • The other settings can be configured based on your needs. (You should review and adjust them accordingly as necessary. Some of them cannot be adjusted again later without deleting and recreating your cluster and/or its node pools, so keep this in mind.) ⚠️

… Once your cluster is running, you'll be able to deploy a Docker image to it below! ✨

Deploy the Docker image

OK, so at this point you need: ☑️

  • Permission to modify a running Kubernetes cluster. (as configured above)
  • The repository of code and a local Golang (Go) environment.
  • Both Docker and the Google Cloud SDK both installed locally on your machine.

… If you're all set, it's time to jump into the terminal to build and deploy the docker image templated out in the repository to the Google Container Registry for use by your Kubernetes cluster. — I've used the project name twice here, so replace “example-grpc” with your project name:

docker build -t gcr.io/example-grpc/example-grpc:demo .
docker push gcr.io/example-grpc/example-grpc:demo

… Then, let's run the image on the cluster:

gcloud components install kubectl

gcloud auth configure-docker
gcloud container clusters get-credentials example-grpc --zone us-central1-a --project=example-grpc

# Format: gke_{project}_{zone}_{cluster}
kubectl config use-context gke_example-grpc_us-central1-a_example-grpc
kubectl run example-grpc --image=gcr.io/example-grpc/example-grpc:demo
kubectl get pods

… Once that's done, you should have the image running on the cluster after a few minutes. You can monitor the status of it on the “Clusters” tab of Kubernetes Engine on Google Cloud Console, or via the command-line.

Redeploying a new image to the cluster later

If you need to replace and redeploy the image again later, just rebuild and push the image with a version name other than the original “demo” label, and then run:

kubectl set image deployment/example-grpc example-grpc=gcr.io/example-grpc/example-grpc:demo2

… That's it. ✅

Configuring the Workload

Once your cluster is running and the image is deployed, let's proceed to configure the “workload” that has been created. — First, we'll be in the terminal, then back in Cloud Console for simplicity.

Configure autoscaling

On the page for your workload, in the “Actions” menu at the top, there's an “Autoscale” configuration option:

The option to configure autoscaling for Google Kubernetes Engine (GKE) workloads.
The option to configure autoscaling for Google Kubernetes Engine (GKE) workloads.

… If you're going to be using your cluster in production or doing heavier testing, you'll want to configure this.

Override the “health check” port and path

… You can skip this step if you won't be running gRPC!

The tricky thing about Ingress, which we'll need to expose our service to the internet, is that by default it will stop serving your application unless the main HTTP service responds with a successful response code (i.e. between 200 and 299, inclusive) on the “/” root path of your HTTP server. — By default, gRPC does not do this!

Luckily, there's a way to override it, but it's not obvious or straightforward. — You can force the health check to another port and/or path alongside your default server if you're planning on running gRPC or some service that does not respond on “/” with this addition to your workload's YAML.

On the Cloud Console page for your workload, click the “YAML” tab, and edit this in under the “spec” → “template” → “spec” → “containers” indentation:

        livenessProbe:
          httpGet:
            path: /_ah/health
            port: 8081
            scheme: HTTPS
        readinessProbe:
          httpGet:
            path: /_ah/health
            scheme: HTTPS
            port: 8081
        ports: # Replace the existing one for this...
        - containerPort: 8080
          protocol: TCP
        - containerPort: 8081
          protocol: TCP

… That's all there is to it. 👍🏻

You might not need this part, but if you'll be running an unmodified gRPC server at this time of writing, your health check will definitely otherwise fail without some other hack to the server software itself.

Exposing the service via Ingress and HTTP/2 + TLS

OK, hopefully final steps here.

… Since there are multiple cluster nodes/pools/pods involved, we'll need to create an Ingress resource, which will create a load balancer automatically that serves as a single client-facing resource. (NOTE: Each Google Load Balancer currently has an unavoidable base cost of around $18 USD/month at this time of writing, even only for very light or experimental usage.)

There are a few different ways to implement Let's Encrypt when running Kubernetes, but many of them require maintaining and configuring your own load balancer. — Doing this instead via Google's own offering is fairly smooth once it's up and running, though! 🤔

Create a “NodePort” for your Workload

First, apply the “manifest/nodeport.yaml” file from the repository to create a NodePort that our Ingress load balancer can point to:

kubectl apply -f manifest/nodeport.yaml

… Note that in this YAML file, it has the same ports as defined in our “livenessProbe” and “readinessProbe” YAML addition to our workload that we set above. (These need to match what we've setup.) — If you skipped that step because you're not serving gRPC, tweak this file to only have the one port you're serving on before applying it.

If you'll be using Let's Encrypt, generate a throwaway, self-signed TLS certificate as a placeholder for Ingress

… If you aren't going to use Let's Encrypt, you can skip this step.

Since HTTP/2 enforces TLS, even if we're using Let's Encrypt at the Ingress level (instead of a certificate we've obtained ourselves from another certificate authority), this whole thing will break down without a TLS certificate of some kind in place from the beginning.

So, one solution to this is to just generate a new authority and certificate locally that lasts for 1,000 years and is for “localhost” or some other hostname never used in production:

mkdir tls

openssl genrsa -des3 -out tls/ca.key 2048
openssl req -x509 -new -nodes -key tls/ca.key -sha256 -days 365000 -out tls/ca.key.pem -subj "/CN=localhost"

openssl req -new -sha256 -nodes -out tls/ca.csr -newkey rsa:2048 -keyout tls/key.pem -subj "/CN=localhost"
openssl x509 -req -in tls/ca.csr -CA tls/ca.key.pem -CAkey tls/ca.key -CAcreateserial -out tls/crt.pem -days 365000 -sha256

After generating these files, convert them to Base64 encoding and then copy-paste the output into the “manifest/ingress-secret.yaml” file:

cat tls/crt.pem | base64
cat tls/key.pem | base64

… OK, that's all the Ingress needs to be created and be “valid,” even though we won't serve anything on its default “frontend configuration” if we're using Let's Encrypt.

Note that these instructions for OpenSSL are for the current version of macOS at this time of writing (10.14.1), so if you're on another platform or version, you might need to update or adjust these slightly to get the same output.

If not using Let's Encrypt, create a static IP address and modify the Ingress manifest file to use it

If you're using a certificate signed by another certificate authority (not Let's Encrypt), you'll want to create a static IP address for your Ingress beforehand using the “gcloud” command-line tool, since you'll be using the default auto-generated frontend configuration for your service in production: (Otherwise, an ephemeral IP address will be auto-assigned, and you could lose that address in the future for some unexpected reason.) ⚠️

gcloud compute addresses create --project=example-grpc --global example-grpc-ipv4

… I included a commented out annotation line for assigning this in the “manifest/ingress.yaml” file, which you can uncomment and adjust based on the name given to the IP address. (above)

Finally, apply the secret and create the Ingress

Copy and paste the Base64-encoded certificate and key into the “manifest/ingress-secret.yaml” file of the repository if you haven't already, and apply this and the “manifest/ingress.yaml” files in that order:

kubectl apply -f manifest/ingress-secret.yaml
kubectl apply -f manifest/ingress.yaml

Cool, now you should see an Ingress creating under your “Load balancers” list on Cloud Console, and your service should begin serving soon… unless you still need Let's Encrypt for TLS! ✅

Configure Ingress for Let's Encrypt

If you're dying to use Let's Encrypt (instead of a manually-renewed certificate obtained from another certificate authority) and have followed the steps above so far to do so, you can add another “frontend configuration” to the Ingress load balancer for your cluster at this point.

… This additional frontend configuration will use the same health check and backend that should already be healthy! — But, a few things to keep in mind:

  • You should not modify or remove the default “frontend configuration” or “backend configuration” that the redeployable “manifest/ingress.yaml” file created, but you should be OK by just adding another frontend configuration. (At this time of writing, reapplying that manifest file will recreate or adjust these default frontend and backend configurations without affecting the other that you've created, but will revert any changes you made to the original configurations manually if you did so.) 🛑
  • It may eventually (later) become possible to use Let's Encrypt immediately when deploying Ingress using the manifest file instead of using the placeholder self-signed certificate, but it is not currently at this time of writing.
  • The default ephemeral IP on the load balancer will still serve the backend using the self-signed certificate we created before, but this is redundant and would have to be explicitly trusted by clients, so it wouldn't be advertised or utilized in production and should just be ignored instead.

… Adding another “frontend configuration” without modifying anything existing is actually super straightforward via “Load balancers” on Cloud Console:

Screenshot with suggested settings for creating a frontend configuration with Let's Encrypt using a Google Cloud managed certificate.
Screenshot with suggested settings for creating a frontend configuration with Let's Encrypt using a Google Cloud managed certificate.

When assigning a certificate to the “frontend configuration” that you're creating, to use Let's Encrypt you can simply select to use a “Google-managed certificate” rather than uploading one manually:

Let's Encrypt is available via Cloud Console using “Google-managed” certificate.
Let's Encrypt is available via Cloud Console by using a “Google-managed” certificate!

… Cool! Once everything has updated, you should now be serving gRPC over TLS with Let's Encrypt on Kubernetes with a passing health check. 👍🏻

]]>
https://benguild.com/2018/11/11/quickstart-golang-kubernetes-grpc-tls-lets-encrypt/ https://benguild.com-/2018/11/11/quickstart-golang-kubernetes-grpc-tls-lets-encrypt Sun, 11 Nov 2018 23:20:35 -0800
Wi-Fi and Cellular Data calling finally comes to Google Voice app on iOS? Today in the iOS app for Google Voice… I saw something that I'd been waiting for for a long time:

A modal offering the option to enable calling over Wi-Fi and Cellular Data in the Google Voice iOS app.
A modal offering the option to enable calling over Wi-Fi and Cellular Data.

Finally! Is everyone else seeing this, too? (although, probably not in Japanese…)

Now, we should be able to place calls just using our iPhone's active internet connection and not the traditional phone system when using the actual Google Voice app, and without needing Hangouts, third-party apps, or Gmail on the web. 🤩

UPDATE: Users on Reddit are reporting that opting-into the beta for internet calling on the web version might be enough to trigger this modal. — If you don't see it appear on iOS, try that first, and then reboot the iOS app! 🤔

]]>
https://benguild.com/2018/09/03/google-voice-ios-mobile-data-wifi/ https://benguild.com-/2018/09/03/google-voice-ios-mobile-data-wifi Mon, 03 Sep 2018 22:33:04 -0700