Skip to content

Commit 2a1d10e

Browse files
authored
UI redesign and merge of Tips and Tutorials (#12)
* Set Tailwind to use dark class for its dark theme * Slightly change light theme * Switch to a Serif font * Make the ToC on Articles look better, increase font size * Merge Tips and Tutorials back together and use tags to distinguish them (R.I.P. SEO) * Fix badge link on Articles list * Add symlinks for Tips and Tutorials and refactor workflow
1 parent 5bdac7d commit 2a1d10e

27 files changed

+308
-259
lines changed

Diff for: .github/workflows/main.yml

+12-1
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,26 @@ jobs:
3232
npm run build
3333
cp vendor/* public/
3434
35-
- name: Push
35+
- name: Bake
3636
run: |
3737
cd $GITHUB_WORKSPACE
3838
git fetch -p
3939
git checkout master
4040
rm -rf config.toml content sass templates
4141
cp -R public/* .
42+
# The two following lines are here to preserve SEO.
43+
ln -s articles tips
44+
ln -s articles tutorials
4245
rm -rf public
46+
47+
- name: Commit
48+
run: |
49+
cd $GITHUB_WORKSPACE
4350
git add -f .
4451
git status
4552
git commit -m "New release"
53+
54+
- name: Push
55+
run: |
56+
cd $GITHUB_WORKSPACE
4657
git push origin master

Diff for: content/tips/20200815-rails-deprecate-activerecord-attribute.md renamed to content/articles/20200815-rails-deprecate-activerecord-attribute.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,29 @@
22
title = "Rails: Deprecate an ActiveRecord attribute"
33
date = 2020-08-15
44
[taxonomies]
5-
tags = ["rails"]
5+
tags = ["tip", "rails"]
66
+++
77

88
## Context
9+
910
When working on a growing application relying on a database, it is not uncommon to have a data model that collects dust: some of its data may become obsolete, but you may not want to delete it just yet, because you may need to migrate it, prepare its deletion later or keep it as history.
1011

1112
You might want to flag this data as deprecated, so that no one (including you) would rely on this no longer updated or relevant data.
1213

1314
## How
15+
1416
You could of course add comments in your code, but there would be no guarantee someone would check those comments before using those attributes. Also, you wouldn't notice anything if existing code would use those attributes indirectly.
1517

1618
Fortunately, Rails provides [a nice way to flag ActiveRecord attributes as obsolete][0], stabilized since Rails 4.x.
1719

1820
Let's create a `Deprecator` class, that'll embed any behavior we'd like to implement. We can be creative here, like:
21+
1922
- logging the action
2023
- logging parts of the stacktrace
2124
- shaming your coworkers on Slack with a bot message
2225

2326
But let's keep it simple for the time being:
27+
2428
```ruby
2529
# app/deprecators/field_deprecator.rb
2630
class FieldDeprecator
@@ -34,6 +38,7 @@ end
3438
We're not respecting the semantics of the second argument, but hey, this is duck-typing :trollface:
3539

3640
Then, all we have to do is to add a call to this class method in our model:
41+
3742
```ruby
3843
class Racoon < ApplicationRecord
3944
deprecate \
@@ -44,11 +49,12 @@ end
4449
```
4550

4651
With our current deprecator, this would output a warning message everytime this field is being read and updated:
52+
4753
```
4854
> Racoon.first.owner_id
4955
owner_id is deprecated, please favor Racoon#caretaker_id
5056
```
5157

52-
*Et voilà.*
58+
_Et voilà._
5359

5460
[0]: https://apidock.com/rails/v4.0.2/Module/deprecate

Diff for: content/tutorials/20200816-developing-on-wsl.md renamed to content/articles/20200816-developing-on-wsl.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22
title = "Developing on WSL"
33
date = 2020-08-16
44
[taxonomies]
5-
tags = ["wsl", "dev"]
5+
tags = ["tutorial", "wsl", "dev"]
66
+++
77

88
## Context
9+
910
The latest versions of Windows 10 have seen the maturation of WSL, the Windows Subsystem for Linux. Basically, it allows installing a Linux distribution that'll use a special Linux kernel running "nativaly" on Windows 10. It doesn't run on a virtualized environment, although it seems to use a Hyper-V volume for storage.
1011

1112
Oh, and you can also use their shiny new [Terminal][0] for ultimate ease of use.
1213

1314
## The Good
1415

1516
### Seamless development
17+
1618
You will evolve in a familiar environment if you're already using VSCode, since it leverages its client/server architecture, each running on Windows and Linux respectively. Typing `code .` in your terminal will open VSCode. Extensions will be installed server-side.
1719

1820
Other than that, you can enjoy the benefits of using any regular Linux distribution.
1921

2022
### You can use Docker
23+
2124
Since the release of WSL 2 (included in the 2004 Windows update), you can use Docker containers inside your Linux distro, managed by your Docker daemon installed on Windows. "All" it requires is using a WSL2 compatible distribution, and enable WSL2 integration in the Docker Desktop preferences.
2225

2326
And, instead of using a virtual machine to host the containers, you can also set Docker Desktop to use... WSL2.
@@ -29,18 +32,21 @@ I haven't made any benchmarks, but I suppose it helps to boost performance.
2932
I suppose you can even use your Kubernetes cluster installed with Docker on Windows from your Linux distribution, but I haven't come that far into testing yet.
3033

3134
### Accessing files between Linux and Windows
35+
3236
Your Windows volumes are automatically mounted in `/mnt`, meaning you can literally access anything from your Windows installation. Also, typing `explorer.exe .` reveals the current folder in Windows' file manager, shown as a network shared volume.
3337

3438
## The Bad
3539

3640
### Occasional bugs and freezes
41+
3742
I do backend development in Ruby (and Rails), Elixir (and mostly Phoenix) professionally, and I also wrote a bit of Elm and Rust in WSL2. I am quite satisfied with the quality of the integration WSL2 offers, but it isn't bug-free either.
3843

39-
For example, when I first tried Zola and ran its web server, I just couldn't connect to it with my browser. Firefox was just displaying "Unable to connect". The reason? I don't know, but running `wsl.exe --shutdown` would "solve" this problem. I'm obviously more annoyed by the fact that I *need* to reboot than the fact that I only lose a few seconds of my time every week or so.
44+
For example, when I first tried Zola and ran its web server, I just couldn't connect to it with my browser. Firefox was just displaying "Unable to connect". The reason? I don't know, but running `wsl.exe --shutdown` would "solve" this problem. I'm obviously more annoyed by the fact that I _need_ to reboot than the fact that I only lose a few seconds of my time every week or so.
4045

4146
But the most annoying thing was occurring during the compilation of my Rust project through Docker (it never happened when I was building "on bare metal"), which was the complete freeze of VSCode, my terminal, and really, any app related to WSL. In that case, I'd need to wait for a few minutes for the build to finish and my system to be responsive again. I assume this has something to do with my building process eating all of the IO reserved for WSL, or something related to this weird Docker setup.
4247

4348
### Wondering about long-term stability
49+
4450
While Microsoft provided a migration script to switch from WSL to WSL2 (through the use of `wsl.exe`, to run in `cmd.exe` or PowerShell), it never worked on the Ubuntu I installed a few months ago, so I tried a few several other distributions (Alpine, Arch) after finally installing a fresh new Ubuntu, which worked instantly with Docker integration. I think not all distributions available on the store are compatible with WSL2, but information is a bit scarce.
4551

4652
So far, my setup has been very stable, but I wonder if a future Windows update could break the whole thing. I got almost anything versioned and hosted somewhere, but still.
@@ -49,7 +55,9 @@ And sure, there's bugtracking through Github, but I think WSL as a whole is pret
4955
## The Ugly
5056

5157
### Get out of my $PATH
58+
5259
To achieve a seemless integration with Windows, my Ubuntu installation has basically the whole Windows mess injected into its default $PATH:
60+
5361
```
5462
➜ / echo $PATH
5563
/home/makks/.asdf/shims:/home/makks/.asdf/bin:/home/makks/.cargo/bin:/home/makks/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Program Files/Git/cmd:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/ProgramData/DockerDesktop/version-bin:/mnt/c/Users/Makks/.cargo/bin:/mnt/c/Users/Makks/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/Makks/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Users/Makks.cargo/bin:/mnt/c/Users/Makks/AppData/Local/GitHubDesktop/bin:/mnt/c/Users/Makks/AppData/Local/Microsoft/WindowsApps
@@ -62,6 +70,7 @@ Whish is... yeah. It itched me in many places when I saw this, and when I saw ra
6270
This kind of dirty black magic may annoy you if you want your distro to be 100% clean. But I guess you wouldn't want to use WSL in the first place in that was the case ;)
6371

6472
## Conclusion
73+
6574
I'm very satisfied with WSL and its related developments so far. My primary reason for using it is the need for good UI and accessibility tools, because I have an extremely low vision (but I'll run into details in a future article) but it's also really satisfying to leverage a good CPU (in that case, a Ryzen 3800X) instead of the traditional coughing overpriced Macbook Pro any startup company grants its employees with.
6675

6776
And of course, you can launch a game or two when you need a break.

Diff for: content/tutorials/20201022-elixir-clustering-on-kubernetes.md renamed to content/articles/20201022-elixir-clustering-on-kubernetes.md

+39-9
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ title = "Elixir: Clustering on Kubernetes"
33
description = "A quick walkthrough to setup a cluster with Elixir, Kubernetes and libcluster."
44
date = 2020-10-21
55
[taxonomies]
6-
tags = ["elixir", "k8s"]
6+
tags = ["tutorial", "elixir", "k8s"]
77
+++
88

99
## Context
10+
1011
When developing Elixir applications, you may want to create a cluster of Erlang nodes at some point, for example, to provide redundancy, high availability,or to share a global state on the cluster (with Mnesia, for example) without hitting an outside DBMS.
1112

1213
This can be easily achieved with `libcluster`. As you can see [in the docs][0], it supports multiple strategies.
@@ -16,17 +17,21 @@ I'll assume you want to deploy to a Kubernetes cluster, and I'll be covering thi
1617
We'll deploy a `cluster` of Erlang `nodes`. To avoid confusion with Kubernetes terminology, I'll use the prefix `k8s` when referring to Kube.
1718

1819
## A word about WSL
20+
1921
You may run into one issue if you're using WSL: by default, WSL systems use the same hostname as their Windows host, which isn't fully qualified, and Erlang may not like that.
2022

2123
To fix this (and to avoid breaking WSL), you can specify a full computer name on Windows.
24+
2225
1. Go to your PC settings (`This PC` -> `Properties` -> `Change Settings` -> `Change`),
2326
2. In the `Computer/Domain Changes` window, keep a simple Computer Name,
2427
3. Click on More, and in the `DNS Suffix and NetBIOS Computer Name` window, specify a primary DNS suffix (like `localdomain`).
2528

2629
You can keep the other settings unchanged, provided you have something like `barney` as computer name and `barney.localdomain` as full computer name.
2730

2831
## Connecting nodes locally
32+
2933
Let's make a new clean project with Mix:
34+
3035
```
3136
> mix new cluster
3237
> cd cluster
@@ -41,57 +46,67 @@ And let's start a `iex` REPL, giving it a new argument:
4146
Here, we just passed a flag to the Erlang VM, specifying the shortname of the node we want to run.
4247

4348
There's now a slight difference appearing in your prompt:
49+
4450
```
4551
iex(a@{HOSTNAME})1>
4652
```
4753

4854
You should see your PC hostname at the right of that `@` symbol. This is a default value, because we haven't specified a fully qualified name.
4955

5056
Let's start a second Elixir app, in another terminal:
57+
5158
```
5259
> iex --sname b
5360
```
5461

5562
And let's discover a few functions!
5663

57-
* `node()` (or `Node.self()`) returns the name of the current Node.
58-
* `Node.list()` returns a list of the connected Nodes in the cluster. At this point, it should be empty.
59-
* `Node.connect()` and `Node.disconnect()` allow you to, you guessed it, connect and disconnect nodes. Let's try it!
64+
- `node()` (or `Node.self()`) returns the name of the current Node.
65+
- `Node.list()` returns a list of the connected Nodes in the cluster. At this point, it should be empty.
66+
- `Node.connect()` and `Node.disconnect()` allow you to, you guessed it, connect and disconnect nodes. Let's try it!
6067

6168
On the `b` Node, type:
69+
6270
```
6371
Node.connect(:a@hostname)
6472
```
6573

6674
Now, go back to the `a` Node, and run:
75+
6776
```
6877
Node.list()
6978
```
7079

7180
You should see `b@hostname` appearing there. Congratulations :) We haven't done anything spectacular this far, but this is exactly what `libcluster` will do under the hood once it's set up correctly.
7281

7382
## Todo
83+
7484
So, here's what we'll have to do:
85+
7586
1. Pass the relevant fully qualified name to each Erlang VM
7687
2. Update our Kubernetes configuration
7788
3. Define a cluster topology to configure `libcluster`
7889

7990
## VM Args
91+
8092
We'll need to pass some arguments to our Erlang VMs.
8193
Since at least its version `1.10`, Mix can handle this (you won't need to add Distillery as a dependency).
8294

8395
In your project directory, run:
96+
8497
```
8598
mix release.init
8699
```
87100

88101
This command will generate a few files. Let's take a look at `vm.args.eex`. It should contain a few commented lines, specifically:
102+
89103
```sh
90104
## Customize flags given to the VM: http://erlang.org/doc/man/erl.html
91105
## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here
92106
```
93107

94108
Since we want to set `name` and deploy on Linux containers, let's get to `env.sh.eex`. There's a few commented lines in there, but we're most interested in the last block:
109+
95110
```sh
96111
# Set the release to work across nodes. If using the long name format like
97112
# the one below ([email protected]), you need to also uncomment the
@@ -101,12 +116,14 @@ Since we want to set `name` and deploy on Linux containers, let's get to `env.sh
101116
```
102117

103118
To enable clustering, we need to replace the `127.0.0.1` part with the fully qualified name of our pod. Kubernetes has its own internal DNS, and pods are typically named like this:
119+
104120
```
105121
# Assuming our Pod IP is 192.168.0.45
106122
192-168-0-45.namespace.pod.cluster.local
107123
```
108124

109125
So your setup should end up looking like this:
126+
110127
```sh
111128
export POD_A_RECORD=$(echo $POD_IP | sed 's/\./-/g')
112129
export RELEASE_DISTRIBUTION=name
@@ -116,7 +133,9 @@ export RELEASE_NODE=myapp@$(echo $POD_A_RECORD).$(echo $NAMESPACE).pod.cluster.l
116133
Both `$POD_IP` and `$NAMESPACE` will have to be defined when our application starts, so we'll add those to our k8s deployment manifest.
117134

118135
## Kubernetes configuration
136+
119137
We should now declare our two new environment variables:
138+
120139
```yaml
121140
apiVersion: apps/v1
122141
kind: Deployment
@@ -155,19 +174,23 @@ spec:
155174
```
156175
157176
Both variables references the pod's information once it's started. You can check it by running:e by running:
177+
158178
```
159179
kubectl get pod myapp-456789 -o yaml
160180
```
161181

162182
If you already have one pod correctly labelled, you can check that libcluster will correctly poll the right pods from Kube by doing what it does: polling with a labelSelector:
183+
163184
```
164185
kubectl get pods -l app=myapp,tier=web
165186
```
166187

167188
If this returns off-topic pods, then you should fix this before proceeding :)
168189

169190
## Libcluster topology
191+
170192
Last, but not least, we need to specify our libcluster strategy:
193+
171194
```elixir
172195
topologies = [
173196
default: [
@@ -190,29 +213,35 @@ topologies = [
190213
Once you deploy this, you should be good :) After scaling up your deployment (`kubectl scale --replicas=X deployment/my-app`), you should see various `[libcluster]` log entries on your pods.
191214

192215
## What now?
216+
193217
There's a few things to toy around with if you want to leverage your cluster!
194218

195219
Starting from there, and after cleaning your configuration and/or setting up cleaner environment variables, there are a few interesting things to do. How about:
196-
* Declaring a GenServer tracking nodes status?
197-
* Implementing a Cluster Singleton worker using [Individual][8]?
198-
* Toying around with Phoenix.PubSub?
199-
* Playing with process registries, like Phoenix.Tracker?
200-
* Discovering Erlang's [Mnesia][9], a powerful in-cluster DBMS to use for internal state or cache, and which makes Redis irrelevant?
220+
221+
- Declaring a GenServer tracking nodes status?
222+
- Implementing a Cluster Singleton worker using [Individual][8]?
223+
- Toying around with Phoenix.PubSub?
224+
- Playing with process registries, like Phoenix.Tracker?
225+
- Discovering Erlang's [Mnesia][9], a powerful in-cluster DBMS to use for internal state or cache, and which makes Redis irrelevant?
201226

202227
## Node up/down notifications
228+
203229
[Erlang exposes a simple function][10] that'll get the current process notified when nodes are up or down. This can allow us to react accordingly, like printing debug information in the logs, dereferencing the node, or push the self destruct button.
204230

205231
In our first example, if you had run the following before connecting the two nodes together:
232+
206233
```elixir
207234
:net_kernel.monitor_nodes(true)
208235
```
209236

210237
Then running `flush` would show you the messages you received:
238+
211239
```elixir
212240
{:nodeup, :a@Hostname}
213241
```
214242

215243
You can call `monitor_nodes` in a [GenServer][11], of course, and implement the relevant callbacks:
244+
216245
```elixir
217246
def handle_info({:nodeup, node}, state) do
218247
IO.puts("[STORE] Node connected: #{inspect node}")
@@ -226,6 +255,7 @@ end
226255
```
227256

228257
## Conclusion
258+
229259
This article is a bit long and rough around the edges, but it should give you a better understanding on how to deploy Elixir applications as a cluster.
230260

231261
I strongly suggest that you follow the official documentations of Elixir, Erlang and libraries. The snippets I included may get outdated over time, though I don't expect the process to be easier than it currently is :)

0 commit comments

Comments
 (0)