Browse Source

replace the custom shortcodes with Markdown and render hooks

The content now uses standard Markdown, rendered by render hooks
(layouts/_markup):

- images: ![alt](image.png "Caption"), rendered as a figure when the
  image is standalone (replaces attachedFigure and screenshotOf)
- links: resolved to a page resource (replaces attachedFileLink in
  links), to a page of the current site or of the other site with a
  language flag (replaces internalLink and relref), and relative links
  are made relative to the site root (they were broken in RSS feeds)
- code blocks: ```yaml {filename="x.yaml" hl_lines="3"} or
  {title="..."} (replaces highlightFile, highlightWithTitle, highlight)

The content was converted with scripts/convert-shortcodes.py, which can
also be used on the drafts. The attachedFileLink shortcode is kept for
the URLs in code blocks, as well as embeddedVideo and youtube.

The generated HTML is the same, except:
- figures are no longer nested in paragraphs (invalid HTML)
- the screenshots of tweets get an alt text
- the language flags are removed from the summaries

Closes #13
Refs #11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YNHsWYTXPThURz6TbVS1j3
master
Nicolas Massé 1 month ago
parent
commit
7fe8344056
  1. 4
      config.yaml
  2. 8
      content/english/_index.md
  3. 4
      content/english/blog/3scale-toolbox-anonymous-policy.md
  4. 8
      content/english/blog/3scale-toolbox-url-rewriting-policy.md
  5. 2
      content/english/blog/airgap-openshift-installation-move-registry-created-using-oc-adm-release-mirror-between-environments/index.md
  6. 28
      content/english/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md
  7. 8
      content/english/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md
  8. 28
      content/english/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md
  9. 34
      content/english/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md
  10. 2
      content/english/blog/configure-redhat-sso-3scale-cli/index.md
  11. 12
      content/english/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md
  12. 8
      content/english/blog/deploy-apis-from-your-cicd-pipeline-with-the-3scale-rest-api.md
  13. 14
      content/english/blog/deploy-openshift-single-node-in-kvm/index.md
  14. 12
      content/english/blog/enable-global-policies-apicast.md
  15. 28
      content/english/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md
  16. 22
      content/english/blog/how-to-run-performance-tests-with-k6-prometheus-grafana/index.md
  17. 2
      content/english/blog/install-openwrt-raspberry-pi/index.md
  18. 12
      content/english/blog/jmeter-assess-software-performances/index.md
  19. 2
      content/english/blog/nginx-with-tls-on-openwrt/index.md
  20. 6
      content/english/blog/use-ansible-to-manage-the-qos-of-your-openshift-workload/index.md
  21. 22
      content/english/blog/use-google-account-openid-connect-provider/index.md
  22. 64
      content/english/blog/use-qlkube-to-query-the-kubernetes-api.md
  23. 8
      content/english/blog/writing-workshop-instructions-with-hugo-deploy-openshift/index.md
  24. 2
      content/english/blog/writing-workshop-instructions-with-hugo/index.md
  25. 4
      content/english/speaking/apidays-2017/index.md
  26. 12
      content/english/speaking/devoteam-tech-for-people-2018/index.md
  27. 21
      content/english/speaking/devoxx-france-2019/index.md
  28. 2
      content/english/speaking/red-hat-forum-2017/index.md
  29. 10
      content/english/speaking/red-hat-open-demo-build-multi-architecture-ci-cd-pipelines-to-run-your-apps-in-the-cloud-and-at-the-edge/index.md
  30. 10
      content/english/speaking/red-hat-open-demo-from-the-esp8266-to-the-openshift-cluster-discover-what-red-hat-can-offer-for-the-edge-computing/index.md
  31. 14
      content/english/speaking/red-hat-open-demo-mission-impossible-stop-the-crazy-train-with-ai-and-edge-before-it-is-too-late/index.md
  32. 26
      content/english/speaking/red-hat-summit-connect-france-2024/index.md
  33. 3
      content/english/speaking/red-hat-tech-exchange-2018/index.md
  34. 3
      content/english/speaking/red-hat-tech-exchange-2019/index.md
  35. 2
      content/english/speaking/red-hat-tech-exchange-2023/index.md
  36. 2
      content/english/speaking/techweek-sg-2019/index.md
  37. 2
      content/english/writing/apis-as-a-product-get-started-in-no-time.md
  38. 8
      content/french/_index.md
  39. 28
      content/french/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md
  40. 8
      content/french/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md
  41. 34
      content/french/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md
  42. 12
      content/french/blog/connect-podman-container-to-openvswitch/index.md
  43. 26
      content/french/blog/decouverte-de-coreos/index.md
  44. 48
      content/french/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8/index.md
  45. 28
      content/french/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md
  46. 14
      content/french/blog/installation-mise-jour-openshift-environnement-deconnecte/index.md
  47. 2
      content/french/speaking/demobox-redhat-sso/index.md
  48. 21
      content/french/speaking/devoxx-france-2019/index.md
  49. 8
      content/french/speaking/devoxx-france-2025/index.md
  50. 8
      content/french/speaking/mad-roadshow-paris-2023/index.md
  51. 10
      content/french/speaking/matinale-it-for-business-devsecops-2022/index.md
  52. 6
      content/french/speaking/openshift-ai-roadshow-2024/index.md
  53. 12
      content/french/speaking/platform-day-2024/index.md
  54. 6
      content/french/speaking/red-hat-allie-strategie-edge/index.md
  55. 9
      content/french/speaking/red-hat-open-tour-lyon-2025/index.md
  56. 6
      content/french/speaking/red-hat-open-tour-toulouse-2024/index.md
  57. 3
      content/french/speaking/red-hat-openshift-user-group-3/index.md
  58. 3
      content/french/speaking/red-hat-openshift-user-group-4/index.md
  59. 7
      content/french/speaking/red-hat-openshift-user-group-5/index.md
  60. 10
      content/french/speaking/red-hat-summit-connect-france-2023/index.md
  61. 26
      content/french/speaking/red-hat-summit-connect-france-2024/index.md
  62. 2
      content/french/speaking/redhat-summit-connect-2021/index.md
  63. 10
      content/french/speaking/riviera-dev-2024/index.md
  64. 2
      content/french/speaking/worldline-tech-forum-explore-france-2024/index.md
  65. 24
      layouts/_markup/render-codeblock.html
  66. 32
      layouts/_markup/render-image.html
  67. 64
      layouts/_markup/render-link.html
  68. 2
      layouts/_partials/article-headline.html
  69. 2
      layouts/_partials/page-description.html
  70. 2
      layouts/search.searchindex.json
  71. 135
      scripts/convert-shortcodes.py

4
config.yaml

@ -6,6 +6,10 @@ enableRobotsTXT: true
capitalizeListTitles: false
markup:
goldmark:
parser:
# Standalone images are rendered as figures by the image render hook
wrapStandAloneImageWithinParagraph: false
highlight:
# Use CSS classes for syntax highlighting. The style sheet is generated with:
# hugo gen chromastyles --style=borland > assets/css/chroma.css

8
content/english/_index.md

@ -3,9 +3,9 @@ title: "Nicolas Massé"
---
I am Nicolas Massé, you will find me on social networks under the nickname "nmasse-itix".
Initially developer, I now work as pre-sales consultant in the fields of [API Management]({{<relref "/topics/api-management" >}}), [Single Sign On]({{<relref "/topics/openid-connect" >}}) and [Containers]({{<relref "/topics/containers" >}}).
Initially developer, I now work as pre-sales consultant in the fields of [API Management](/topics/api-management), [Single Sign On](/topics/openid-connect) and [Containers](/topics/containers).
I mainly work with [Open Source]({{<relref "/opensource" >}}) technologies such as [Keycloak]({{<relref "/opensource/keycloak" >}}), [3scale]({{<relref "/opensource/3scale" >}}) or [OpenShift]({{<relref "/opensource/openshift" >}}).
I share my [expertise]({{<relref "/topics" >}}) and interests on this site, as a guest [on other websites]({{<relref "/writing" >}}) and I occasionally give [conferences]({{<relref "/speaking" >}}).
I mainly work with [Open Source](/opensource) technologies such as [Keycloak](/opensource/keycloak), [3scale](/opensource/3scale) or [OpenShift](/opensource/openshift).
I share my [expertise](/topics) and interests on this site, as a guest [on other websites](/writing) and I occasionally give [conferences](/speaking).
Please read [my blog]({{<relref "/blog" >}}) or [learn more about me](https://www.linkedin.com/in/nicolasmasse/).
Please read [my blog](/blog) or [learn more about me](https://www.linkedin.com/in/nicolasmasse/).

4
content/english/blog/3scale-toolbox-anonymous-policy.md

@ -89,7 +89,7 @@ Import an OpenAPI Specification file that contains no security requirements.
Confirm your API has been imported as an *Open API*: it contains the *Anonymous* policy.
{{< highlight raw "hl_lines=4-11" >}}
```raw {hl_lines="4-11"}
$ curl -s "https://${TENANT}-admin.3scale.net/admin/api/nginx/spec.json?access_token=${TOKEN}"|jq '.services[]|select(.system_name == "toolbox_open_api")|.proxy.policy_chain'
[
@ -107,7 +107,7 @@ $ curl -s "https://${TENANT}-admin.3scale.net/admin/api/nginx/spec.json?access_t
"configuration": {}
}
]
{{< / highlight >}}
```
Now, import an OpenAPI Specification file that contains a global security requirement **mandating API Key security**.

8
content/english/blog/3scale-toolbox-url-rewriting-policy.md

@ -21,14 +21,14 @@ By default, the public and private basepath have the same value and are taken fr
Let's examine a concrete example.
You have an actual API implementation live at **/camel/my-route** and an API contract as follow:
{{< highlight yaml "hl_lines=5" >}}
```yaml {hl_lines="5"}
swagger: '2.0'
info:
title: 'Beer Catalog API'
[...]
basePath: /camel/my-route
[...]
{{< / highlight >}}
```
If you wish to expose it publicly *as-is* on **/camel/my-route**, there is nothing special to do, the toolbox will do the right thing.
@ -41,14 +41,14 @@ But if you want to expose it publicly on **/api/v1**, you will have to pass the
Let's examine another example.
You have an API contract stating that you want to expose your API publicly on **/api/v1**
{{< highlight yaml "hl_lines=5" >}}
```yaml {hl_lines="5"}
swagger: '2.0'
info:
title: 'Beer Catalog API'
[...]
basePath: /api/v1
[...]
{{< / highlight >}}
```
If unfortunately your API backend is live at another *Base Path* (**/camel/my-route**), you will have to pass the following option to the *3scale import openapi* command:

2
content/english/blog/airgap-openshift-installation-move-registry-created-using-oc-adm-release-mirror-between-environments/index.md

@ -147,7 +147,7 @@ Finally, skopeo is called to download each image to */tmp/oci_registry*.
with_items: '{{ images }}'
```
The complete playbook [is available here]({{< attachedFileLink src="pull.yaml" >}}) and can be run as follow.
The complete playbook [is available here](pull.yaml) and can be run as follow.
```sh
ansible-playbook pull.yaml -e token=$TOKEN

28
content/english/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md

@ -16,7 +16,7 @@ resources:
- '*.gif'
---
At the {{< internalLink path="/speaking/red-hat-summit-connect-france-2024/index.md" >}}, I led a workshop for developers entitled "**Open Code Quest**".
At the [](/speaking/red-hat-summit-connect-france-2024/index.md), I led a workshop for developers entitled "**Open Code Quest**".
In this workshop, developers had to code microservices using Quarkus, OpenShift and an Artificial Intelligence service: IBM's Granite model.
The workshop was designed as a speed competition: the first to complete all three exercises received a reward.
@ -74,7 +74,7 @@ And that's precisely what we're interested in:
And if the three previous conditions are true, we can deduce that the user has completed and validated the **hero** exercise.
Over time, these time series progress as shown in the figure below.
{{< attachedFigure src="exercise-validation.png" title="When the three conditions are met, the exercise is validated." >}}
![When the three conditions are met, the exercise is validated.](exercise-validation.png "When the three conditions are met, the exercise is validated.")
That's a good start, isn't it?
If you do the same thing for all three exercises, you can see who has completed the whole workshop.
@ -83,7 +83,7 @@ Given that some exercises take longer than others, we could imagine awarding mor
This is the approach I've tried to model in the figure below, with a weighting of 55 for the first exercise, 30 for the second and 45 for the last.
The idea is to approximate a linear progression of points over time (1 point per minute).
{{< attachedFigure src="counting-scheme-no-time.png" title="Progression of the number of points for a normal, slow and fast user over time and with each exercise weighted according to the nominal duration of the exercise." >}}
![Progression of the number of points for a normal, slow and fast user over time and with each exercise weighted according to the nominal duration of the exercise.](counting-scheme-no-time.png "Progression of the number of points for a normal, slow and fast user over time and with each exercise weighted according to the nominal duration of the exercise.")
It's starting to come together.
But if you look closely, at the end of the workshop (at the 150th minute), all the participants have finished and have the same score.
@ -107,12 +107,12 @@ Couldn't an accelerator be activated each time an exercise is validated, giving
That would make the competition more engaging and more fun!
And that's what I've tried to model in the diagram below.
{{< attachedFigure src="counting-scheme-with-time.png" title="Progression of the number of points for a normal, slow and fast user over time and with accelerator and weighting of each exercise according to the time it takes the user to complete the exercise." >}}
![Progression of the number of points for a normal, slow and fast user over time and with accelerator and weighting of each exercise according to the time it takes the user to complete the exercise.](counting-scheme-with-time.png "Progression of the number of points for a normal, slow and fast user over time and with accelerator and weighting of each exercise according to the time it takes the user to complete the exercise.")
Now the question is: does a user who takes the lead in the first exercise gain a significant advantage that would make the competition unbalanced?
We found the answer during the various rehearsals that took place at Red Hat before D-Day.
{{< attachedFigure src="counting-scheme-dry-run.png" title="Validation of the point counting model during a dry-run." >}}
![Validation of the point counting model during a dry-run.](counting-scheme-dry-run.png "Validation of the point counting model during a dry-run.")
In the screenshot above, you can see that Batman completed the "hero" exercise **late**.
But by completing the "villain" exercise **very quickly**, he was able to take back the lead... **temporarily**.
@ -168,7 +168,7 @@ Data should now appear in the Grafana dashboard.
To enjoy this to the full, stop the `run.sh` script by pressing **Ctrl + C** and run it again!
After a few seconds, you should see fresh data appear on the dashboard, as in the video below.
{{< attachedFigure src="leaderboard-simulation.gif" title="Simulation of the Open Code Quest workshop on the testing workbench to validate the point-counting system (video accelerated 10x)." >}}
![Simulation of the Open Code Quest workshop on the testing workbench to validate the point-counting system (video accelerated 10x).](leaderboard-simulation.gif "Simulation of the Open Code Quest workshop on the testing workbench to validate the point-counting system (video accelerated 10x).")
### Prometheus queries
@ -226,7 +226,7 @@ EOF
Copy and paste the query into the **Explore** section of Grafana and you should get the following graph.
{{< attachedFigure src="grafana-explore-opencodequest-leaderboard-hero.png" title="The metric \"opencodequest_leaderboard_hero:prod\" represents the completeness status of the exercise \"hero\" in the environment \"prod\"." >}}
![The metric "opencodequestleaderboardhero:prod" represents the completeness status of the exercise "hero" in the environment "prod".](grafana-explore-opencodequest-leaderboard-hero.png "The metric \"opencodequest_leaderboard_hero:prod\" represents the completeness status of the exercise \"hero\" in the environment \"prod\".")
It should be read as follows (note: 1728646377 = 13:32:57):
@ -289,7 +289,7 @@ It may seem complex at first, but in fact it's not that complex.
To understand how this query works, I suggest you split it into two parts: the `increase(...)` part on one side and the rest on the other.
We overlay this with the previous query and we get the following figure.
{{< attachedFigure src="grafana-opencodequest-leaderboard-onetime-bonus.png" title="The metric \"opencodequest_leaderboard_hero_onetime_bonus:prod\" represents the time bonus allocated to a user when they complete the \"hero\" exercise in the \"prod\" environment." >}}
![The metric "opencodequestleaderboardheroonetimebonus:prod" represents the time bonus allocated to a user when they complete the "hero" exercise in the "prod" environment.](grafana-opencodequest-leaderboard-onetime-bonus.png "The metric \"opencodequest_leaderboard_hero_onetime_bonus:prod\" represents the time bonus allocated to a user when they complete the \"hero\" exercise in the \"prod\" environment.")
From top to bottom, we can see:
@ -319,7 +319,7 @@ This can be seen as the integral of the time serie.
The following figure shows how this query works in more detail.
{{< attachedFigure src="grafana-opencodequest-leaderboard-lifetime-bonus.png" title="The metric \"opencodequest_leaderboard_hero_lifetime_bonus:prod\" represents the carry-over of the time bonus allocated to a user when he completes the exercise \"hero\" in the environment \"prod\"." >}}
![The metric "opencodequestleaderboardherolifetimebonus:prod" represents the carry-over of the time bonus allocated to a user when he completes the exercise "hero" in the environment "prod".](grafana-opencodequest-leaderboard-lifetime-bonus.png "The metric \"opencodequest_leaderboard_hero_lifetime_bonus:prod\" represents the carry-over of the time bonus allocated to a user when he completes the exercise \"hero\" in the environment \"prod\".")
From top to bottom, we can observe:
@ -364,7 +364,7 @@ In fact, the 3 accelerators already add up, so the user gains 1 point every 5 mi
The following figure shows the 6 Prometheus query components used to calculate the user's score.
{{< attachedFigure src="grafana-opencodequest-leaderboard.png" title="The 6 components of the Prometheus query calculating user scores and the final result." >}}
![The 6 components of the Prometheus query calculating user scores and the final result.](grafana-opencodequest-leaderboard.png "The 6 components of the Prometheus query calculating user scores and the final result.")
### Recording Rules
@ -438,7 +438,7 @@ The `${user:regex}` syntax allows Grafana to replace `user=~"${user:regex}"` wit
To show the instant ranking, I used the **Bar Chart** visualisation with a **Sort by** transformation on the **Value** field.
{{< attachedFigure src="grafana-opencodequest-leaderboard-instant-snapshot.png" title="Grafana visualisation settings for instant ranking." >}}
![Grafana visualisation settings for instant ranking.](grafana-opencodequest-leaderboard-instant-snapshot.png "Grafana visualisation settings for instant ranking.")
The important parameters of this visualisation are :
@ -450,7 +450,7 @@ The important parameters of this visualisation are :
To track the progression of scores over time, I have opted for the **Time series** visualisation.
{{< attachedFigure src="grafana-opencodequest-leaderboard-points-over-time.png" title="Grafana visualisation settings for score progression." >}}
![Grafana visualisation settings for score progression.](grafana-opencodequest-leaderboard-points-over-time.png "Grafana visualisation settings for score progression.")
The important parameters of this visualisation are :
@ -476,7 +476,7 @@ As for the question on everyone's lips: did superheroes fight it out for the pod
The answer is a resounding **YES!**
And there were plenty of thrills when the results were announced...
{{< attachedFigure src="grafana-opencodequest-points.png" title="Progression of the Open Code Quest 74 participants' scores." >}}
![Progression of the Open Code Quest 74 participants' scores.](grafana-opencodequest-points.png "Progression of the Open Code Quest 74 participants' scores.")
Take a look at all those intersecting curves, all those superheroes competing for first place!
@ -488,4 +488,4 @@ The project not only highlighted technologies such as Quarkus, OpenShift and IBM
Designing the Leaderboard, although complex, added a motivating competitive dimension to the workshop.
On the day, watching the participants compete for speed while exploring Red Hat solutions was incredibly gratifying.
To find out how I implemented this Leaderboard in a multi-cluster architecture using Red Hat ACM, please visit: {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md" >}}.
To find out how I implemented this Leaderboard in a multi-cluster architecture using Red Hat ACM, please visit: [](/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md).

8
content/english/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md

@ -17,7 +17,7 @@ resources:
- '*.gif'
---
After revealing the behind-the-scenes design of the Leaderboard for the "Open Code Quest" workshop during the {{< internalLink path="/speaking/red-hat-summit-connect-france-2024/index.md" >}}, it's time to delve deeper into its practical implementation!
After revealing the behind-the-scenes design of the Leaderboard for the "Open Code Quest" workshop during the [](/speaking/red-hat-summit-connect-france-2024/index.md), it's time to delve deeper into its practical implementation!
In this article, I'm going to take you through the configuration of **Red Hat Advanced Cluster Management** as well as the various adaptations needed to connect the *Leaderboard* created earlier with the **Open Code Quest** infrastructure.
@ -25,7 +25,7 @@ Come on board with me for this new stage, which is more technical than the previ
<!--more-->
This article follows on from {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}.
This article follows on from [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md).
If you haven't read it yet, I advise you to read it first to understand the context better.
## Prometheus queries
@ -227,7 +227,7 @@ Observability is an additional module (in the sense that it is not installed by
The following diagram shows the architecture of the observability module in **Red Hat Advanced Cluster Management**.
I created it by observing the relationships between the components from an installation of ACM version 2.11.
{{< attachedFigure src="redhat-acm-observability-architecture.svg" title="Logical architecture of observability in Red Hat Advanced Cluster Management 2.11" >}}
![Logical architecture of observability in Red Hat Advanced Cluster Management 2.11](redhat-acm-observability-architecture.svg "Logical architecture of observability in Red Hat Advanced Cluster Management 2.11")
The components deployed on the central cluster are in **green**, those deployed on the managed clusters are in **blue** and the configuration items are in **grey**.
I've also illustrated the two possible places for calculating *recording rules*, in **yellow**.
@ -429,7 +429,7 @@ read -q "?press any key to continue "
./switch-to-grafana-admin.sh "$(oc whoami)"
```
Then create the "Red Hat Summit Connect 2024" dashboard, as explained in the article {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}.
Then create the "Red Hat Summit Connect 2024" dashboard, as explained in the article [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md).
And finally, export the dashboard in the form of a ConfigMap.

28
content/english/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md

@ -93,7 +93,7 @@ kubectl -n ci apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/t
Create the **buildah** Task in the **ci** namespace as follow.
{{< highlightFile "task.yaml" "yaml" "hl_lines=9-10 25-29 31 44-46 64-66" >}}
```yaml {filename="task.yaml" hl_lines="9-10 25-29 31 44-46 64-66"}
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
@ -164,7 +164,7 @@ spec:
push "$TARGET_IMAGE:latest"
exit 0
{{< /highlightFile >}}
```
The important parts of the task have been highlighted:
@ -175,7 +175,7 @@ The important parts of the task have been highlighted:
Create the **buildah-multiarch** Tekton Pipeline in the **ci** namespace as follow.
The highlighted part of the pipeline contains the list of the target architectures.
{{< highlightFile "pipeline.yaml" "yaml" "hl_lines=9-13" >}}
```yaml {filename="pipeline.yaml" hl_lines="9-13"}
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
@ -228,13 +228,13 @@ spec:
subPath: containers
taskRef:
name: buildah
{{< /highlightFile >}}
```
If the target container registry requires authentication to push a container image, you will need to create a **Service Account** and a **Secret**.
Create the **tekton-robot** Secret in the **ci** namespace as follow.
{{< highlightFile "serviceaccount.yaml" "yaml" "" >}}
```yaml {filename="serviceaccount.yaml"}
apiVersion: v1
kind: ServiceAccount
metadata:
@ -243,11 +243,11 @@ secrets:
- name: quay-authentication
imagePullSecrets:
- name: quay-authentication
{{< /highlightFile >}}
```
Create the secret to authenticate against your target registry (quay.io in my case) as follow.
{{< highlightFile "secret.yaml" "yaml" "" >}}
```yaml {filename="secret.yaml"}
apiVersion: v1
kind: Secret
metadata:
@ -255,13 +255,13 @@ metadata:
data:
.dockerconfigjson: '[REDACTED]'
type: kubernetes.io/dockerconfigjson
{{< /highlightFile >}}
```
Note: you can get this secret, by creating a [Robot Account](https://docs.quay.io/glossary/robot-accounts.html) under your [Organization](https://docs.quay.io/glossary/organizations.html).
Then, you can assign it **write** permissions on the target repository.
Finally, you can click on your robot account and download the Kubernetes secret.
{{< attachedFigure src="quay-robot-account.png" title="Download the Kubernetes secret of your Quay robot account." >}}
![Download the Kubernetes secret of your Quay robot account.](quay-robot-account.png "Download the Kubernetes secret of your Quay robot account.")
At this stage, if you run the pipeline, it will fail and complains it cannot run ARMv8 binaries on a x84_64 host (**Exec format error**).
@ -269,7 +269,7 @@ At this stage, if you run the pipeline, it will fail and complains it cannot run
Deploy **Qemu** on all the nodes of your Kubernetes cluster by creating the **multiarch-qemu** DaemonSet in the namespace of you choice as follow.
{{< highlightFile "daemonset.yaml" "yaml" "" >}}
```yaml {filename="daemonset.yaml"}
apiVersion: apps/v1
kind: DaemonSet
metadata:
@ -292,7 +292,7 @@ spec:
- /register --reset --persistent yes && while :; do sleep 3600; done
securityContext:
privileged: true
{{< /highlightFile >}}
```
This DaemonSet will run a container that will configure Qemu, on each node of your Kubernetes cluster.
@ -323,7 +323,7 @@ That's the magic behind it!
Create the **PipelineRun** in the **ci** namespace to start the pipeline.
Do not forget to change the **outputContainerImage** parameter to match the URL of your container registry!
{{< highlightFile "pipelinerun.yaml" "yaml" "hl_lines=13" >}}
```yaml {filename="pipelinerun.yaml" hl_lines="13"}
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
@ -346,7 +346,7 @@ spec:
resources:
requests:
storage: 1Gi
{{< /highlightFile >}}
```
You can follow the pipeline execution with the **tkn** command.
@ -356,7 +356,7 @@ tkn -n ci pipelineruns logs -f
Once the pipeline finished, on my registry (Quay.io), I could see the container images for both architectures under the "latest" tag.
{{< attachedFigure src="quay-repository.png" title="On Quay.io, you can see the container images for both architectures: ARMv8 and x86_64." >}}
![On Quay.io, you can see the container images for both architectures: ARMv8 and x8664.](quay-repository.png "On Quay.io, you can see the container images for both architectures: ARMv8 and x86_64.")
## Conclusion

34
content/english/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md

@ -15,7 +15,7 @@ resources:
- '*.svg'
---
In 2022, I wrote an article on this subject named {{< internalLink path="/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md" >}}.
In 2022, I wrote an article on this subject named [](/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md).
The article described the configuration I had set up for my personal projects.
It even went beyond its initial purpose as it has also been used by several colleagues at Red Hat who had the same need.
While the configuration described in this previous article is still relevant, the approach is somewhat dated.
@ -56,7 +56,7 @@ While the previous article targeted vanilla Kubernetes platforms, in this one I
In the Tekton pipelines, I use **Buildah** to build and push the container images to the registry.
{{< attachedFigure src="overview.svg" title="Overview of the implemented configuration." >}}
![Overview of the implemented configuration.](overview.svg "Overview of the implemented configuration.")
## Deployment of OpenShift on AWS in Multi-Architecture Mode
@ -105,7 +105,7 @@ curl -sfL https://mirror.openshift.com/pub/openshift-v4/multi/clients/ocp/4.15.1
Create the **install-config.yaml** file.
The highlighted areas are the important points of the file.
{{< highlightFile "install-config.yaml" "yaml" "hl_lines=10 15 26 29" >}}
```yaml {filename="install-config.yaml" hl_lines="10 15 26 29"}
additionalTrustBundlePolicy: Proxyonly
apiVersion: v1
baseDomain: aws.itix.cloud
@ -154,7 +154,7 @@ publish: External
pullSecret: 'REDACTED'
sshKey: |
REDACTED
{{< /highlightFile >}}
```
I chose to keep the declaration of the Compute nodes but with replicas set to 0 because this simplifies the instantiation of the **x86_64** Compute node later on.
@ -169,7 +169,7 @@ As the [OpenShift 4.15 documentation](https://docs.openshift.com/container-platf
Edit the **manifests/cvo-overrides.yaml** file and add the **additionalEnabledCapabilities** section.
Do not copy and paste the entire file below because the **clusterID** is specific to your installation!
{{< highlightFile "manifests/cvo-overrides.yaml" "yaml" "hl_lines=10-21" >}}
```yaml {filename="manifests/cvo-overrides.yaml" hl_lines="10-21"}
apiVersion: config.openshift.io/v1
kind: ClusterVersion
metadata:
@ -191,7 +191,7 @@ spec:
- OperatorLifecycleManager
- Build
- DeploymentConfig
{{< /highlightFile >}}
```
Start the cluster installation on AWS.
@ -362,7 +362,7 @@ At this point, you should have three nodes in your cluster:
- 1 Compute node **x86_64**
- 1 Compute node **arm64**
{{< attachedFigure src="openshift-console-three-nodes-x86_64-and-arm64.png" title="OpenShift console showing three nodes: two x86_64 nodes and one arm64." >}}
![OpenShift console showing three nodes: two x8664 nodes and one arm64.](openshift-console-three-nodes-x86_64-and-arm64.png "OpenShift console showing three nodes: two x86_64 nodes and one arm64.")
## Tekton operator installation
@ -453,7 +453,7 @@ spec:
Create an EFS volume by following the steps in the [AWS documentation](https://docs.aws.amazon.com/efs/latest/ug/gs-step-two-create-efs-resources.html).
{{< attachedFigure src="aws-console-efs.png" title="AWS console showing an EFS volume." >}}
![AWS console showing an EFS volume.](aws-console-efs.png "AWS console showing an EFS volume.")
Create the **StorageClass** associated with the EFS volume.
The **fileSystemId** field matches to the second column in the screenshot above.
@ -476,7 +476,7 @@ parameters:
Following the documentation [OpenShift 4.15](https://docs.openshift.com/container-platform/4.15/storage/container_storage_interface/persistent-storage-csi-aws-efs.html#efs-create-volume_persistent-storage-csi-aws-efs), modify the EFS **Security Group** to allow OpenShift nodes to access storage via the NFS protocol.
{{< attachedFigure src="aws-console-security-group.png" title="AWS console showing the EFS Security Group configuration." >}}
![AWS console showing the EFS Security Group configuration.](aws-console-security-group.png "AWS console showing the EFS Security Group configuration.")
At this point, the AWS EFS service is accessible from the OpenShift cluster but is not yet usable by Tekton pipelines.
Indeed, Tekton's **affinity-assistant** prohibits an RWX persistent volume from being mounted in two places at the same time (error *"more than one PersistentVolumeClaim is bound"*).
@ -533,7 +533,7 @@ The specifics of this task are related to the prerequisites necessary for runnin
- The `STORAGE_DRIVER=vfs` environment variable disables the use of **overlayfs**.
- Finally, the capability **SETFCAP** is assigned to **buildah**.
{{< highlightFile "tekton/task-buildah-build.yaml" "yaml" "hl_lines=19-20 25-26 33-35 37-38" >}}
```yaml {filename="tekton/task-buildah-build.yaml" hl_lines="19-20 25-26 33-35 37-38"}
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
@ -572,7 +572,7 @@ spec:
volumeMounts:
- name: container-storage
mountPath: /var/lib/containers
{{< /highlightFile >}}
```
The script executed by this task is concise (only two steps):
@ -638,11 +638,11 @@ The first use case presented at the beginning of this article is about the use o
The corresponding pipeline fetches the source code, executes a **buildah build** for each architecture (**x86_64** and **arm64**) and pushes the manifest to the registry.
{{< attachedFigure src="tekton-pipeline-containerfile.png" title="OpenShift console showing running the multi-architecture Tekton pipeline (Containerfile building a container image of the `htop` utility)." >}}
![OpenShift console showing running the multi-architecture Tekton pipeline (Containerfile building a container image of the htop utility).](tekton-pipeline-containerfile.png "OpenShift console showing running the multi-architecture Tekton pipeline (Containerfile building a container image of the `htop` utility).")
The resulting image on the quay.io registry clearly shows a manifest containing two images: one for **x86_64** and one for **arm64**.
{{< attachedFigure src="quay-image-containerfile.png" title="Quay console showing a multi-architecture image of the `htop` utility)." >}}
![Quay console showing a multi-architecture image of the htop utility).](quay-image-containerfile.png "Quay console showing a multi-architecture image of the `htop` utility).")
### The "NodeJS" pipeline
@ -651,7 +651,7 @@ The NodeJS pipeline is significantly different in that the Git repository is fet
The reason is that the **npm install** command will download the application dependencies and some of these dependencies can embed native code (this is the case for some low-level libraries).
Therefore, we need a separate *workspace* for each architecture.
{{< attachedFigure src="tekton-pipeline-nodejs.png" title="OpenShift console showing running the multi-architecture Tekton pipeline building a NodeJS application." >}}
![OpenShift console showing running the multi-architecture Tekton pipeline building a NodeJS application.](tekton-pipeline-nodejs.png "OpenShift console showing running the multi-architecture Tekton pipeline building a NodeJS application.")
### The "Quarkus" Pipeline
@ -659,7 +659,7 @@ The Quarkus pipeline is different from the NodeJS pipeline in the sense that the
The bytecode contained in Jar files can run on any architecture.
Therefore there is only one Git workspace shared between all architectures.
{{< attachedFigure src="tekton-pipeline-quarkus.png" title="OpenShift console showing running the multi-architecture Tekton pipeline building a Quarkus application." >}}
![OpenShift console showing running the multi-architecture Tekton pipeline building a Quarkus application.](tekton-pipeline-quarkus.png "OpenShift console showing running the multi-architecture Tekton pipeline building a Quarkus application.")
### Link a task to an architecture
@ -667,7 +667,7 @@ So far the article does not say how Tekton organizes itself to execute the task
There is no magic! It's all happening in the **PipelineRun**!
{{< highlightFile "examples/htop/tekton/pipelinerun.yaml" "yaml" "hl_lines=41-49" >}}
```yaml {filename="examples/htop/tekton/pipelinerun.yaml" hl_lines="41-49"}
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
@ -717,7 +717,7 @@ spec:
podTemplate:
nodeSelector:
beta.kubernetes.io/arch: arm64
{{< /highlightFile >}}
```
The **taskRunSpecs** field allows you to add execution constraints to one or more pipeline tasks.

2
content/english/blog/configure-redhat-sso-3scale-cli/index.md

@ -98,7 +98,7 @@ You can use the following URL for the **OpenID Connect Issuer** (replace $SSO_HO
https://zync:s3cr3t@$SSO_HOSTNAME/auth/realms/3scale
```
{{< attachedFigure src="openid-connect-issuer.png" title="OpenID Connect Issuer settings in the 3scale Admin Portal." >}}
![OpenID Connect Issuer settings in the 3scale Admin Portal.](openid-connect-issuer.png "OpenID Connect Issuer settings in the 3scale Admin Portal.")
Or when [deploying an API in 3scale with the 3scale toolbox](https://developers.redhat.com/blog/2019/07/29/3scale-toolbox-deploy-an-api-from-the-cli/), you can use:

12
content/english/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md

@ -48,7 +48,7 @@ Hence, if you try to query your container by its DNS name from your host, it wil
By configuring a third dnsmasq instance to gather the libvirt and podman ones, we achieve consistent DNS name resolution.
The beauty of this setup is that you do not have to maintain this third dnsmasq instance since there is one provided with NetworkManager, and we will re-use it!
{{< attachedFigure src="dnsmasq-for-consistent-dns-resolution-vm-containers.svg" title="Three dnsmasq instances to get consistent DNS name resolution between VMs, containers and the host." >}}
![Three dnsmasq instances to get consistent DNS name resolution between VMs, containers and the host.](dnsmasq-for-consistent-dns-resolution-vm-containers.svg "Three dnsmasq instances to get consistent DNS name resolution between VMs, containers and the host.")
## Configure Podman
@ -113,7 +113,7 @@ This also prevents the infamous 5 seconds timeout on IPv6 DNS queries.
Create a file **/tmp/default-net.xml** that contains the following libvirt network definition.
{{< highlightFile "/tmp/default-net.xml" "xml" "" >}}
```xml {filename="/tmp/default-net.xml"}
<network xmlns:dnsmasq="http://libvirt.org/schemas/network/dnsmasq/1.0">
<name>default</name>
<forward mode='nat'>
@ -132,7 +132,7 @@ Create a file **/tmp/default-net.xml** that contains the following libvirt netwo
<dnsmasq:option value="auth-zone=dns.libvirt"/>
</dnsmasq:options>
</network>
{{< /highlightFile >}}
```
Replace the default libvirt network.
@ -180,11 +180,11 @@ options edns0 trust-ad
Create the dnsmasq configuration file to dispatch requests to **dns.podman**, **dns.libvirt** and **dns.host**.
{{< highlightFile "/etc/NetworkManager/dnsmasq.d/podman-libvirt-dns.conf" "ini" "" >}}
```ini {filename="/etc/NetworkManager/dnsmasq.d/podman-libvirt-dns.conf"}
server=/dns.podman/10.88.0.1
server=/dns.libvirt/192.168.122.1
host-record=host.dns.host,192.168.122.1
{{< /highlightFile >}}
```
Restart the dnsmasq instance of NetworkManager.
@ -237,6 +237,6 @@ dig +short www.google.fr
If your configuration is correct, you should get the following result. The top left terminal is the container, the bottom left terminal is the virtual machine and the right terminal is the host.
{{< attachedFigure src="consistent-dns-resolution-in-vm-and-containers.png" title="Consistent DNS name resolution between VMs, containers and the host." >}}
![Consistent DNS name resolution between VMs, containers and the host.](consistent-dns-resolution-in-vm-and-containers.png "Consistent DNS name resolution between VMs, containers and the host.")
Congratulations! You just configured a consistent DNS name resolution between VMs, containers and the host.

8
content/english/blog/deploy-apis-from-your-cicd-pipeline-with-the-3scale-rest-api.md

@ -933,11 +933,11 @@ jq '.cors[0].configuration' policies.json > cors_schema.json
Create a sample configuration for the **cors** policy.
{{< highlightFile "cors.json" "json" "" >}}
```json {filename="cors.json"}
{
"allow_credentials": true
}
{{< /highlightFile >}}
```
Validate that the configuration conforms to the extracted schema.
@ -948,7 +948,7 @@ jsonschema -i cors.json cors_schema.json
Create the final policy chain.
{{< highlightFile "policy_chain.json" "json" "" >}}
```json {filename="policy_chain.json"}
[
{
"name": "cors",
@ -965,7 +965,7 @@ Create the final policy chain.
"enabled": true
}
]
{{< /highlightFile >}}
```
You can then change the policy chain with the **Proxy Policies Chain Update** method.
For instance, update the policy chain of the **echo** service with the new policy chain.

14
content/english/blog/deploy-openshift-single-node-in-kvm/index.md

@ -21,7 +21,7 @@ This article explores how to deploy OpenShift Single Node (SNO) using KVM on you
On your Fedora workstation, make sure you installed the required packages [as explained in the Fedora documentation](https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-virtualization/).
Configure {{<internalLink path="/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md" title="NetworkManager to use dnsmasq" >}} for DNS resolution.
Configure [NetworkManager to use dnsmasq](/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md) for DNS resolution.
Choose a base domain name and a cluster name.
The base domain does not need to be public or registered somewhere.
@ -44,7 +44,7 @@ You can get one for free by registering as a developer [here](https://developers
Create the libvirt network definition.
{{< highlightFile "ocp-net.xml" "xml" "" >}}
```xml {filename="ocp-net.xml"}
<network xmlns:dnsmasq="http://libvirt.org/schemas/network/dnsmasq/1.0">
<name>ocp-dev</name>
<forward mode='nat'>
@ -72,7 +72,7 @@ Create the libvirt network definition.
<dnsmasq:option value="cname=*.apps.itix-dev.ocp.itix,lb.itix-dev.ocp.itix"/>
</dnsmasq:options>
</network>
{{< /highlightFile >}}
```
This file defines a libvirt network named **ocp-dev** and add the required DNS records for a standard installation of OpenShift.
Make sure to replace **itix-dev.ocp.itix** by your cluster name and base domain.
@ -106,7 +106,7 @@ Configure the cluster.
* Check **I want to install single node OpenShift (SNO)**
* Click **Next**
{{< attachedFigure src="assisted-installer-1.png" title="Assisted Installer: configure the cluster." >}}
![Assisted Installer: configure the cluster.](assisted-installer-1.png "Assisted Installer: configure the cluster.")
Generate the ISO image.
@ -131,7 +131,7 @@ On cloud.redhat.com, wait for the node to appear in the list.
* Click **Next**.
{{< attachedFigure src="assisted-installer-2.png" title="Assisted Installer: wait for the node to appear in the list." >}}
![Assisted Installer: wait for the node to appear in the list.](assisted-installer-2.png "Assisted Installer: wait for the node to appear in the list.")
* Select your Network Subnet in the dropdown list.
* Click **Next**.
@ -140,10 +140,10 @@ On cloud.redhat.com, wait for the node to appear in the list.
You can follow the installation process from cloud.redhat.com.
Once the installation is complete, download the **kubeadmin** password and make sure you can access the OpenShift Console.
{{< attachedFigure src="assisted-installer-5.png" title="Assisted Installer: the installation is complete." >}}
![Assisted Installer: the installation is complete.](assisted-installer-5.png "Assisted Installer: the installation is complete.")
Also, make sure you can login with SSH to your OpenShift node.
If you configured NetworkManager and dnsmasq {{<internalLink path="/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md" title="as explained here" >}}, you should be able to reach your OpenShift node by its DNS name.
If you configured NetworkManager and dnsmasq [as explained here](/blog/consistent-dns-name-resolution-for-virtual-machines-and-containers/index.md), you should be able to reach your OpenShift node by its DNS name.
Otherwise, use the IP address.
```sh

12
content/english/blog/enable-global-policies-apicast.md

@ -24,7 +24,7 @@ Start from those default *Environment Files* and add a `policy_chain` field with
The default *Global Policy Chain* can be found in the [`gateway/src/apicast/policy_chain.lua`](https://github.com/3scale/APIcast/blob/b8f7f067dd47936f93bc9bd3e6de224c304d58ea/gateway/src/apicast/policy_chain.lua#L67-L72) file.
**production.lua:**
{{< highlight lua "hl_lines=8-14" >}}
```lua {hl_lines="8-14"}
return {
master_process = 'on',
lua_code_cache = 'on',
@ -40,10 +40,10 @@ return {
'apicast.policy.nginx_metrics'
}),
}
{{< / highlight >}}
```
**staging.lua:**
{{< highlight lua "hl_lines=7-13" >}}
```lua {hl_lines="7-13"}
return {
master_process = 'on',
lua_code_cache = 'on',
@ -58,12 +58,12 @@ return {
'apicast.policy.nginx_metrics'
}),
}
{{< / highlight >}}
```
Then, create a ConfigMap from those two files and mount it in `/opt/app-root/src/config`:
{{< highlight sh >}}
```sh
oc create configmap apicast-cors --from-file=production.lua --from-file=staging.lua
oc set volume dc/apicast-production --add --name=apicast-cors -t configmap --configmap-name=apicast-cors -m /opt/app-root/src/config
oc set volume dc/apicast-staging --add --name=apicast-cors -t configmap --configmap-name=apicast-cors -m /opt/app-root/src/config
{{< / highlight >}}
```

28
content/english/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md

@ -33,9 +33,9 @@ This **HP DL20 Gen9** server is equipped with :
- 2 x 3.5" disks, 4 TB each, configured as RAID 1 using the HP RAID card
- 6 x 1 GbE RJ-45 ports
{{< attachedFigure src="hp-dl20-gen9.webp" title="The existing HP DL20 Gen9 server." >}}
![The existing HP DL20 Gen9 server.](hp-dl20-gen9.webp "The existing HP DL20 Gen9 server.")
This server is currently racked in a 7U, short depth IT rack in the basement of the house and is currently running **CentOS Stream 8** which I have configured as a router (see {{< internalLink path="/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8" >}}) and hypervisor (with **libvirt**).
This server is currently racked in a 7U, short depth IT rack in the basement of the house and is currently running **CentOS Stream 8** which I have configured as a router (see [](/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8)) and hypervisor (with **libvirt**).
The hypervisor runs several VMs:
@ -68,7 +68,7 @@ The expansion capabilities of the **HP DL20 Gen9** are quite limited: 2 PCIe x8
The full-height slot is taken by the RAID controller and the half-height slot is taken by the 4-port GbE network card.
Last but not least, the motherboard and CPU of the **HP DL20 Gen9** do not support **SR-IOV**. This currently forces me to run network routing functions directly on the hypervisor.
SR-IOV would allow me to put the {{<internalLink path="/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8" title="CentOS Stream router" >}} into a VM without performance loss.
SR-IOV would allow me to put the [CentOS Stream router](/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8) into a VM without performance loss.
To sum up, I need :
@ -143,7 +143,7 @@ Then it will still be time to upgrade to a more powerful CPU.
The **Ampere Altra Q64-22** CPU has a TDP of 69 W according to the [Ampere Altra datasheet](https://d1o0i0v5q5lp8h.cloudfront.net/ampere/live/assets/documents/Altra_Rev_A1_DS_v1.27_20220331.pdf).
{{< attachedFigure src="ampere-altra-q64-22.jpeg" title="The Ampere Altra Q64-22 CPU, mounted on the ASRock Rack ALTRAD8UD-1L2T motherboard" >}}
![The Ampere Altra Q64-22 CPU, mounted on the ASRock Rack ALTRAD8UD-1L2T motherboard](ampere-altra-q64-22.jpeg "The Ampere Altra Q64-22 CPU, mounted on the ASRock Rack ALTRAD8UD-1L2T motherboard")
Another difficulty is that I live in France, and Newegg doesn't ship to France...
So I had to go through a reshipper: a company located the USA that receives packages and ships them all over the world.
@ -187,7 +187,7 @@ Before deciding to buy the [Innovision M24306](http://iovserver.com/2u-server-ca
The **Innovision M24306** is available on [Aliexpress](https://www.aliexpress.us/item/1005005856237609.html) and [Alibaba](https://www.alibaba.com/product-detail/Ultra-Short-2U-rackmount-Server-Chassis_1600636420062.html).
{{< attachedFigure src="innovision-m24306.jpeg" title="The Innovision M24306 case." >}}
![The Innovision M24306 case.](innovision-m24306.jpeg "The Innovision M24306 case.")
Some useful notes (I asked for clarification before ordering):
@ -200,7 +200,7 @@ Some useful notes (I asked for clarification before ordering):
Note: the VGA connector on the server's right ear is not connected to anything.
But this isn't a problem in the sense that the case has its inputs/outputs on the front side, so the VGA connector is already on the front side and doesn't need to be remoted.
{{< attachedFigure src="vga-not-connected.jpeg" title="The VGA socket on the right ear of the Innovision M24306 is not connected." >}}
![The VGA socket on the right ear of the Innovision M24306 is not connected.](vga-not-connected.jpeg "The VGA socket on the right ear of the Innovision M24306 is not connected.")
## FSP CRPS power supply
@ -228,7 +228,7 @@ The [FSP-FC250](https://www.fsp-group.com/download/pro/FSP-FC250_Datasheet.pdf),
I then opted for the lowest-powered power supply module available, as 2 x 550W is already a lot for a small server!
This is the [FSP550-20FM](https://www.fsp-group.com/download/pro/FSP550-20FM_Datasheet.pdf) model.
{{< attachedFigure src="psu-fsp.webp" title="The composition of the FSP CRPS power supply." >}}
![The composition of the FSP CRPS power supply.](psu-fsp.webp "The composition of the FSP CRPS power supply.")
Please note that the Power Distribution Board is only fixed to the case by two small screws.
To prevent it from moving, I had to shim it with a piece of foam.
@ -257,7 +257,7 @@ The reference **CAB-8654/8654-8i-11-P0.5M-85** from [10Gtek](https://www.10gtek.
- Finally, you can opt for angled or straight connectors.
I chose the **straight connectors**.
{{< attachedFigure src="slimsas-cables.webp" title="SlimSAS / SFF-8654 cable routing in the Innovision M24306 box." >}}
![SlimSAS / SFF-8654 cable routing in the Innovision M24306 box.](slimsas-cables.webp "SlimSAS / SFF-8654 cable routing in the Innovision M24306 box.")
## Storage
@ -271,7 +271,7 @@ These SSDs are in *Add-in Card (AIC)*, half-height, half-length (**HHHL**) forma
But for the time being, it keeps me going.
And if I find an eBay auction with a batch of Samsung PM1733 SSDs at a good price, I'll just have to transfer the data to the new SSDs to get 3 PCIe slots back.
{{< attachedFigure src="nvme-storage.jpeg" title="Three Samsung PM1735 SSDs and one Samsung 980 PRO SSD installed in the case." >}}
![Three Samsung PM1735 SSDs and one Samsung 980 PRO SSD installed in the case.](nvme-storage.jpeg "Three Samsung PM1735 SSDs and one Samsung 980 PRO SSD installed in the case.")
It should be noted that the **Samsung PM1733A** SSDs have been tested by **Ampere Computing** and have shown good performance with their CPU! See [Samsung FIO Performance - Solution Brief](https://amperecomputing.com/briefs/samsung-FIO-performance).
@ -279,7 +279,7 @@ On top of that, I've added an NVMe SSD in M.2 format to host the operating syste
I also tested six U.2 format (PCIe 3.0) NVMe SSDs on the case's NVMe backplane. And it works! However, I didn't test the hotplug feature, as I don't think the case's backplane has it implemented.
{{< attachedFigure src="more-nvme-storage.webp" title="Six more NVMe SSDs, for a total of 26.4 TB of NVMe storage!" >}}
![Six more NVMe SSDs, for a total of 26.4 TB of NVMe storage!](more-nvme-storage.webp "Six more NVMe SSDs, for a total of 26.4 TB of NVMe storage!")
## Bill of Materials
@ -307,7 +307,7 @@ And in this price, I haven't counted shipping costs, customs duties, etc. 💸
It should be noted that, by default, the airflow of the **FSP 550-20FM** power supply and **Innovision M24306** case fans is reversed: the power supply fans blow in a **front to back** direction, while the case fans blow in a **back to front** direction.
Fortunately, the case fans can be reversed.
{{< attachedFigure src="fans-air-flow.webp" title="I flipped all four fans to have front to back airflow. In this configuration, the fan labels are not visible from inside the case." >}}
![I flipped all four fans to have front to back airflow. In this configuration, the fan labels are not visible from inside the case.](fans-air-flow.webp "I flipped all four fans to have front to back airflow. In this configuration, the fan labels are not visible from inside the case.")
I haven't done a performance test yet, but the temperatures measured are lower with this optimized airflow.
The effect is most noticeable in the 6 NVMe SSDs on the left.
@ -320,15 +320,15 @@ The three unused slots can be masked to force airflow to where it's most needed.
The external appearance of the product is rather flattering, almost professional.
{{< attachedFigure src="final-product.webp" title="2U server based on an Innovision M24306 case, an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU. Front view is on the top, rear view is on the bottom." >}}
![2U server based on an Innovision M24306 case, an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU. Front view is on the top, rear view is on the bottom.](final-product.webp "2U server based on an Innovision M24306 case, an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU. Front view is on the top, rear view is on the bottom.")
The interior isn't as well designed as an HP or Dell server, but I'd be quite happy with it. 😎
{{< attachedFigure src="internal-layout.webp" title="Internal layout of the Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU." >}}
![Internal layout of the Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU.](internal-layout.webp "Internal layout of the Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU.")
Last photos, this time with the cover in place, ready to be racked! 🚀
{{< attachedFigure src="perspective-view.webp" title="Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU." >}}
![Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU.](perspective-view.webp "Innovision M24306 case with an ASRock Rack ALTRAD8UD-1L2T motherboard and an Ampere Altra Q64-22 CPU.")
## Conclusion

22
content/english/blog/how-to-run-performance-tests-with-k6-prometheus-grafana/index.md

@ -27,7 +27,7 @@ This lack has been identified by the community and is tracked through the [GitHu
In the meantime, we can use StatsD as a bridge between K6 and Prometheus since K6 has a native support for StatsD and Prometheus handle the StatsD protocol through its [statsd_exporter](https://github.com/prometheus/statsd_exporter).
{{< attachedFigure src="k6-statsd-prometheus.svg" title="K6 pushes its metrics to the statsd_exporter while Prometheus scrapes the statsd_exporter." >}}
![K6 pushes its metrics to the statsdexporter while Prometheus scrapes the statsdexporter.](k6-statsd-prometheus.svg "K6 pushes its metrics to the statsd_exporter while Prometheus scrapes the statsd_exporter.")
K6 has two implementations of the StatsD protocol: statsd and datadog.
The datadog implementation has the advantage of enabling the tags extension in the StatsD protocol.
@ -40,7 +40,7 @@ This article explains how to set it up on Fedora using Podman.
Create the statsd_exporter configuration file.
{{< highlightFile "statsd_exporter.yaml" "yaml" "" >}}
```yaml {filename="statsd_exporter.yaml"}
defaults:
observer_type: histogram
mappings:
@ -52,7 +52,7 @@ mappings:
http_name: "$1"
check_name: "$2"
outcome: "$3"
{{< /highlightFile >}}
```
This configuration instructs statsd_exporter to:
@ -69,7 +69,7 @@ Create the Prometheus configuration that scrapes the statsd_exporter every secon
It also filters out some labels that are not used in the Grafana dashboard.
It is important to filter out the **url** label since it can lead to [high cardinalities](https://www.robustperception.io/cardinality-is-key) in Prometheus.
{{< highlightFile "prometheus.yaml" "yaml" "" >}}
```yaml {filename="prometheus.yaml"}
global:
scrape_interval: 1s
evaluation_interval: 1s
@ -82,7 +82,7 @@ scrape_configs:
metric_relabel_configs:
- regex: '(job|instance|url)'
action: labeldrop
{{< /highlightFile >}}
```
Start the Prometheus server.
@ -108,7 +108,7 @@ CONTAINER ID IMAGE COMMAND C
Open [http://localhost:9090/targets](http://localhost:9090/targets) and make sure Prometheus can scrape the statsd_exporter.
{{< attachedFigure src="prometheus-targets.png" title="Prometheus successfully scrapes the statsd_exporter." >}}
![Prometheus successfully scrapes the statsdexporter.](prometheus-targets.png "Prometheus successfully scrapes the statsd_exporter.")
Open [http://localhost:3000/](http://localhost:3000/) and login with **admin** / **admin**.
@ -150,7 +150,7 @@ sudo dnf install k6
There was a couple Grafana dashboards available in the community but none of them matched my requirements.
So I designed my own that features measurement of the seven standard Go HTTP timings and tracking of multiple K6 HTTP request (by their name).
{{< attachedFigure src="k6-grafana-dashboard.png" title="Screenshot of the Grafana dashboard while K6 is conducting a performance test." >}}
![Screenshot of the Grafana dashboard while K6 is conducting a performance test.](k6-grafana-dashboard.png "Screenshot of the Grafana dashboard while K6 is conducting a performance test.")
The dashboard is divided in three parts:
@ -183,7 +183,7 @@ You can try the whole stack with this simple K6 test.
It starts by defining the number of VUs over three stages: ramp-up, steady, ramp-down.
It adds error counting (how many JavaScript exceptions during a test run), a very simple test function and wraps this test function with error counting.
{{< highlightFile "simple-test.js" "js" "" >}}
```js {filename="simple-test.js"}
import http from "k6/http";
import { check, sleep } from "k6";
import { Rate } from "k6/metrics";
@ -223,7 +223,7 @@ function simpleTest() {
}
export default wrapWithErrorCounting(simpleTest);
{{< /highlightFile >}}
```
You can run the test with the **k6 run** command.
@ -233,13 +233,13 @@ k6 run -o datadog simple-test.js
If everything went fine, you should see the k6 summary in the console.
{{< attachedFigure src="k6-simple-test2.png" title="K6 prints a summary upon completion." >}}
![K6 prints a summary upon completion.](k6-simple-test2.png "K6 prints a summary upon completion.")
In the grafana dashboard, select **simple-test** in the **K6 request name** dropdown list and **req_duration** in the **Go HTTP Metric Name** dropdown list.
Note: you might have to hit **CTRL-R** to reload the page if the dropdown list is empty.
Then, the grafana dashboard should look like this.
{{< attachedFigure src="k6-simple-test.png" title="The grafana dashboard shows a K6 test run." >}}
![The grafana dashboard shows a K6 test run.](k6-simple-test.png "The grafana dashboard shows a K6 test run.")
## Conclusion

2
content/english/blog/install-openwrt-raspberry-pi/index.md

@ -72,7 +72,7 @@ screen /dev/ttyUSB0 115200
You then need to connect the VIN, GND, TXD and RXD wires to the correct GPIO pins of your Raspberry PI.
{{< attachedFigure src="uart-pins.jpeg" title="The UART Pins of the Raspberry PI 3" >}}
![The UART Pins of the Raspberry PI 3](uart-pins.jpeg "The UART Pins of the Raspberry PI 3")
Power-on your Raspberry PI, wait a couple seconds and press enter to display the OpenWRT prompt.

12
content/english/blog/jmeter-assess-software-performances/index.md

@ -104,7 +104,7 @@ http {
My first measure on the control (nginx) gave strange results.
{{< attachedFigure src="control1.png" title="First measure gave results too low to be representative of nginx's performances." >}}
![First measure gave results too low to be representative of nginx's performances.](control1.png "First measure gave results too low to be representative of nginx's performances.")
Less than 30 tps, even on an old ARM board, is definitely too low to be representative of nginx's performances.
Using [Wireshark](https://www.wireshark.org/), I discovered that JMeter did not established [Keep-Alive connections](https://sqa.stackexchange.com/questions/38211/re-using-the-tcp-connections-with-jmeter-like-a-real-browser).
@ -137,13 +137,13 @@ httpclient.reset_state_on_thread_group_iteration=false
With Keep-Alive enabled, the 1200 tps are much more inline with the known performance level of nginx.
Except there is a performance drop every 10-15 seconds that is not expected.
{{< attachedFigure src="control2.png" title="Second measure shows a periodic performance drop." >}}
![Second measure shows a periodic performance drop.](control2.png "Second measure shows a periodic performance drop.")
Using Wireshark, I discovered that during those performance drops, there are [TCP packets retransmissions](https://wiki.wireshark.org/DuplicatePackets).
At that time, my Macbook Pro hosting the injector was connected **using Wifi**.
I switched to a good old Ethernet cable, and this time the results on the control were as expected.
{{< attachedFigure src="control3.png" title="Third measure is ok." >}}
![Third measure is ok.](control3.png "Third measure is ok.")
As a conclusion, always have a control in your experience!
@ -151,7 +151,7 @@ As a conclusion, always have a control in your experience!
To build my JMeter Test Plan, I started by adding a **User Defined Variables** component that holds all the settings related to lab environment (DNS names, ports, tokens, etc.).
{{< attachedFigure src="udv.png" title="JMeter User Defined Variables" >}}
![JMeter User Defined Variables](udv.png "JMeter User Defined Variables")
I added a variable named **scenario** whose value will be passed to JMeter from the CLI.
This enables me to run all my experiments automatically from a script, one after another.
@ -163,12 +163,12 @@ ${__P(parameter-name,default-value)}
I configured a **Thread Group** based on the **jp@gc - Ultimate Thread Group** with five concurrent users.
{{< attachedFigure src="thread-group.png" title="JMeter Thread Group" >}}
![JMeter Thread Group](thread-group.png "JMeter Thread Group")
And finally, a **Loop** component with three **If Controllers** underneath.
Each **If Controller** holds an **HTTP Probe** configured for the target scenario.
{{< attachedFigure src="if.png" title="JMeter If Controller" >}}
![JMeter If Controller](if.png "JMeter If Controller")
The If Controllers are defined with a **jexl3** expression, asserting the value of the **scenario** variable.

2
content/english/blog/nginx-with-tls-on-openwrt/index.md

@ -87,7 +87,7 @@ Enter **Global Build Settings** and:
* Press space to unset **Select all userspace packages by default**
* Leave **Cryptographically sign packages** set
{{< attachedFigure src="make-menuconfig.png" title="Parameters to activate in your make menuconfig" >}}
![Parameters to activate in your make menuconfig](make-menuconfig.png "Parameters to activate in your make menuconfig")
Go back to the root menu.

6
content/english/blog/use-ansible-to-manage-the-qos-of-your-openshift-workload/index.md

@ -168,7 +168,7 @@ in order to bring back those objects to the Best Effort QoS class.
Since I do not want all Pods to have the Best Effort QoS class, I added a
blacklist of critical namespaces that should not be touched.
{{< highlight raw "hl_lines=5-10 23" >}}
```raw {hl_lines="5-10 23"}
- name: Change the QoS class of commodity projects
hosts: localhost
gather_facts: no
@ -192,8 +192,8 @@ blacklist of critical namespaces that should not be touched.
loop_control:
loop_var: obj
when: obj.namespace not in namespace_blacklist
{{< / highlight >}}
```
You can find the complete playbook [here]({{< attachedFileLink src="change-qos.yaml" >}}). Of course, it is
You can find the complete playbook [here](change-qos.yaml). Of course, it is
very rough and would need to more work to be used on a daily basis but for a
single use this is sufficient.

22
content/english/blog/use-google-account-openid-connect-provider/index.md

@ -50,15 +50,15 @@ First, connect to the [Google Developer Console](https://console.developers.goog
Click **Create Project**.
{{< attachedFigure src="create-project.png" title="Click 'Create Project'." >}}
![Click 'Create Project'.](create-project.png "Click 'Create Project'.")
Fill-in the **Project name** (free choice). The location does not matter. Click **Create**.
{{< attachedFigure src="project-name.png" title="Fill-in the 'Project name'. Click 'Create'." >}}
![Fill-in the 'Project name'. Click 'Create'.](project-name.png "Fill-in the 'Project name'. Click 'Create'.")
Click **OAuth Consent screen**. If you are a Google Suite user, select **Internal**. If you are a regular GMail user, select **External**. Click **Create**.
{{< attachedFigure src="oauth-consent.png" title="The OAuth Consent screen." >}}
![The OAuth Consent screen.](oauth-consent.png "The OAuth Consent screen.")
Choose an application name (free choice).
Leave the default scopes.
@ -66,15 +66,15 @@ Add your personal domain to the list of **Authorized Domains**.
For instance, if your target service is at *raspberry-pi.example.test*, add **example.test**.
**DO NOT FORGET to press Enter!**
{{< attachedFigure src="authorized-domains.png" title="Add your personal domain to the list of Authorized Domains." >}}
![Add your personal domain to the list of Authorized Domains.](authorized-domains.png "Add your personal domain to the list of Authorized Domains.")
Fill-in the **Application Homepage Link** and **Application Privacy Policy Link** (free choices). Click **Save**.
{{< attachedFigure src="links.png" title="Fill-in the links." >}}
![Fill-in the links.](links.png "Fill-in the links.")
Click **Credentials**. Select **+ Create Credentials**, then **OAuth Client ID**.
{{< attachedFigure src="create-credentials.png" title="Create the OAuth credentials." >}}
![Create the OAuth credentials.](create-credentials.png "Create the OAuth credentials.")
Under **Application type**, select **Web Application**.
Choose a name for your application (free choice).
@ -86,7 +86,7 @@ To be able to do so, we need to add a special Redirect URI: **http://localhost:6
Click **Create**.
{{< attachedFigure src="redirect-uri.png" title="Fill-in the Redirect URI." >}}
![Fill-in the Redirect URI.](redirect-uri.png "Fill-in the Redirect URI.")
Google generated a **Client ID** and **Client Secret** for you. Keep them somewhere safe!
@ -116,15 +116,15 @@ Now, run this script!
The script generates a URL that you need to copy and paste in your web browser.
{{< attachedFigure src="script-start.png" title="The script generates a URL that you need to copy and paste in your web browser." >}}
![The script generates a URL that you need to copy and paste in your web browser.](script-start.png "The script generates a URL that you need to copy and paste in your web browser.")
If you are not yet logged in, Google asks you to authenticate.
{{< attachedFigure src="auth.png" title="If you are not yet logged in, Google asks you to authenticate." >}}
![If you are not yet logged in, Google asks you to authenticate.](auth.png "If you are not yet logged in, Google asks you to authenticate.")
Once logged in or if you are already logged in, you are redirected to the fake Redirect URI we registered earlier.
{{< attachedFigure src="auth-ok.png" title="You are redirected to the fake Redirect URI we registered earlier" >}}
![You are redirected to the fake Redirect URI we registered earlier](auth-ok.png "You are redirected to the fake Redirect URI we registered earlier")
We registered a fake Redirect URI so that we could play each part of the OpenID Connect exchange manually.
**So, if you see an error message from your web browser saying that it cannot connect to the target service: THIS IS EXPECTED FOR OUR TEST.**
@ -134,7 +134,7 @@ Once the browser is redirected at `http://localhost:666/stop-here`, copy the red
Remember that the Authorization Code is very short lived.
So, be quick!
{{< attachedFigure src="script-url.png" title="Paste the Redirect URI." >}}
![Paste the Redirect URI.](script-url.png "Paste the Redirect URI.")
The script contacts the Authorization Server to get an Access Token from the Authorization Code captured in the Redirect URI.

64
content/english/blog/use-qlkube-to-query-the-kubernetes-api.md

@ -16,34 +16,34 @@ It is very useful for mobile application and web development: by reducing the nu
To install QLKube in OpenShift, use the NodeJS Source-to-Image builder:
{{< highlight sh >}}
```sh
oc new-project qlkube --display-name=QLKube
oc new-app nodejs~https://github.com/qlkube/qlkube.git --name=qlkube
{{< / highlight >}}
```
Disable TLS certificate validation to accommodate your self-signed certificates:
{{< highlight sh >}}
```sh
oc set env dc/qlkube NODE_TLS_REJECT_UNAUTHORIZED=0
{{< / highlight >}}
```
And enable the NodeJS development mode to enable the GraphQL explorer (disabled in production mode):
{{< highlight sh >}}
```sh
oc set env dc/qlkube NODE_ENV=development
{{< / highlight >}}
```
Give the GLKube's Service Account the right to query the Kubernetes API for its own namespace:
{{< highlight sh >}}
```sh
oc adm policy add-role-to-user view -z default
{{< / highlight >}}
```
Once deployed, open the QLKube URL in your web browser:
{{< highlight sh >}}
```sh
open $(oc get route qlkube -o go-template --template="http://{{.spec.host}}")
{{< / highlight >}}
```
You can try the following queries in the GraphQL explorer.
@ -53,7 +53,7 @@ Unless you gave the `cluster-admin` right to the QLKube Service Account, you wil
**Query:**
{{< highlight graphql >}}
```graphql
query getAllPodsInCurrentNamespace {
all(namespace: "qlkube") {
pods {
@ -69,11 +69,11 @@ query getAllPodsInCurrentNamespace {
}
}
}
{{< / highlight >}}
```
**Response:**
{{< highlight json >}}
```json
{
"data": {
"all": {
@ -102,7 +102,7 @@ query getAllPodsInCurrentNamespace {
}
}
}
{{< / highlight >}}
```
## Get a service by name
@ -110,7 +110,7 @@ To get an object by name, you can use the `fieldSelector` parameter (in this exa
**Query:**
{{< highlight graphql >}}
```graphql
query getServiceByNameAndNamespace {
all(namespace: "qlkube", fieldSelector: "metadata.name=qlkube") {
services {
@ -126,11 +126,11 @@ query getServiceByNameAndNamespace {
}
}
}
{{< / highlight >}}
```
**Response:**
{{< highlight json >}}
```json
{
"data": {
"all": {
@ -150,14 +150,14 @@ query getServiceByNameAndNamespace {
}
}
}
{{< / highlight >}}
```
## Type introspection
Playing with the built-in types of GLKube is nice but you might soon be limited.
To discover all the available types, run this query:
{{< highlight graphql >}}
```graphql
{
__schema {
types {
@ -165,11 +165,11 @@ To discover all the available types, run this query:
}
}
}
{{< / highlight >}}
```
This query returns a list of all the available types (truncated here for brevity):
{{< highlight json >}}
```json
{
"data": {
"__schema": {
@ -199,7 +199,7 @@ This query returns a list of all the available types (truncated here for brevity
}
}
}
{{< / highlight >}}
```
## Get a Deployment Config by name and namespace
@ -210,7 +210,7 @@ Once the desired data type discovered, you can use it directly.
**Query:**
{{< highlight graphql >}}
```graphql
query getDeploymentConfigByNameAndNamespace {
comGithubOpenshiftApiAppsV1DeploymentConfig(name: "qlkube", namespace: "qlkube") {
metadata {
@ -222,11 +222,11 @@ query getDeploymentConfigByNameAndNamespace {
}
}
}
{{< / highlight >}}
```
**Reponse:**
{{< highlight json >}}
```json
{
"data": {
"comGithubOpenshiftApiAppsV1DeploymentConfig": {
@ -240,7 +240,7 @@ query getDeploymentConfigByNameAndNamespace {
}
}
}
{{< / highlight >}}
```
## Get routes by hostname and namespace
@ -248,7 +248,7 @@ This query use a `fieldSelector` on the `host` field in the `spec` section and u
**Query:**
{{< highlight graphql >}}
```graphql
query getRouteByHostnameAndNamespace {
routes: comGithubOpenshiftApiRouteV1RouteList(namespace: "qlkube" fieldSelector: "spec.host=qlkube-qlkube.app.itix.fr") {
items {
@ -266,11 +266,11 @@ query getRouteByHostnameAndNamespace {
}
}
}
{{< / highlight >}}
```
**Reponse:**
{{< highlight json >}}
```json
{
"data": {
"routes": {
@ -296,13 +296,13 @@ query getRouteByHostnameAndNamespace {
}
}
}
{{< / highlight >}}
```
## Sending your GraphQL request from curl
Once your GraphQL queries refined in the GraphQL Explorer, you can send them directly using curl or any HTTP client.
{{< highlight sh >}}
```sh
export GLKUBE_HOSTNAME=$(oc get route qlkube -o go-template --template="{{.spec.host}}")
cat <<EOF | curl -XPOST "http://$GLKUBE_HOSTNAME/" -H "Content-Type: application/json" -d @- -s |jq .
@ -323,7 +323,7 @@ cat <<EOF | curl -XPOST "http://$GLKUBE_HOSTNAME/" -H "Content-Type: application
}"
}
EOF
{{< / highlight >}}
```
## Advanced use-cases

8
content/english/blog/writing-workshop-instructions-with-hugo-deploy-openshift/index.md

@ -168,25 +168,25 @@ First, get the GitHub Webhook URL of your BuildConfig:
- Open the **Configuration** tab
- Copy the **GitHub Webhook URL**
{{< attachedFigure src="openshift-buildconfig-webhook.png" title="The BuildConfig on OpenShift." >}}
![The BuildConfig on OpenShift.](openshift-buildconfig-webhook.png "The BuildConfig on OpenShift.")
Then, go on your GitHub repository and add a webhook:
- Go to **Settings** > **Webhooks**
- Click **Add webhook**
{{< attachedFigure src="github-add-webhook.png" title="Add a webhook on GitHub." >}}
![Add a webhook on GitHub.](github-add-webhook.png "Add a webhook on GitHub.")
- Paste your Webhook URL in the **Payload URL** field
- Select `application/json` in the **Content type** dropdown list
- Check **Disable** in the **SSL verification** section if your OpenShift console has a self-signed certificate
- Click **Add webhook**
{{< attachedFigure src="github-webhook.png" title="The GitHub webhook." >}}
![The GitHub webhook.](github-webhook.png "The GitHub webhook.")
Try to push some changes to your GitHub repository and see OpenShift rebuilding and deploying your website!
{{< attachedFigure src="openshift-rebuild.png" title="OpenShift rebuilding and redeploying your website" >}}
![OpenShift rebuilding and redeploying your website](openshift-rebuild.png "OpenShift rebuilding and redeploying your website")
## Conclusion

2
content/english/blog/writing-workshop-instructions-with-hugo/index.md

@ -201,7 +201,7 @@ git push -u origin master
Congratulations! You now have your training instructions neatly organised and
clearly presented. Maintenance and collaboration have been greatly simplified!
{{< attachedFigure src="hugo-screenshot.png" title="Screenshot of our mini-training." >}}
![Screenshot of our mini-training.](hugo-screenshot.png "Screenshot of our mini-training.")
In this first part of the series, we presented a very light introduction to
Hugo and its application to workshop instructions. Be sure to read [part 2: Writing workshop instructions with Hugo, with variables in your content](../writing-workshop-instructions-with-hugo-variables/) to discover advanced usages.

4
content/english/speaking/apidays-2017/index.md

@ -9,7 +9,7 @@ draft: false
On the 30th January 2018, I spoke at API Days on a session named
_"An API Journey: from Mock to Deployment workshop"_.
{{< screenshotOf src="tweet-jylls35.png" href="https://twitter.com/jylls35/status/958303326638141440" >}}
{{< screenshotOf src="tweet-RedHatFrance.png" href="https://twitter.com/RedHatFrance/status/958633464932241408" >}}
[![Tweet by @jylls35](tweet-jylls35.png)](https://twitter.com/jylls35/status/958303326638141440)
[![Tweet by @RedHatFrance](tweet-RedHatFrance.png)](https://twitter.com/RedHatFrance/status/958633464932241408)
For more information: http://www.apidays.io/events/paris-2017

12
content/english/speaking/devoteam-tech-for-people-2018/index.md

@ -10,7 +10,11 @@ The 10th Septembre 2018, I co-presented a session named _"No API, No Future"_
in which I exhibited the Red Hat's API Lifecycle Automation. The event
`#TechForPeople` was organized by Devoteam.
{{< attachedFigure src="2018-10-10-Devoteam-TechForPeople-1.jpg" >}}
{{< attachedFigure src="2018-10-10-Devoteam-TechForPeople-2.jpg" >}}
{{< attachedFigure src="2018-10-10-Devoteam-TechForPeople-3.jpg" >}}
{{< attachedFigure src="2018-10-10-Devoteam-TechForPeople-4.jpg" >}}
![](2018-10-10-Devoteam-TechForPeople-1.jpg)
![](2018-10-10-Devoteam-TechForPeople-2.jpg)
![](2018-10-10-Devoteam-TechForPeople-3.jpg)
![](2018-10-10-Devoteam-TechForPeople-4.jpg)

21
content/english/speaking/devoxx-france-2019/index.md

@ -18,18 +18,21 @@ On the 19th April 2019, I spoke at Devoxx France on a session named _"Une API, d
## Pictures
{{< attachedFigure src="on-stage-1.jpg" >}}
{{< attachedFigure src="on-stage-2.jpg" >}}
{{< attachedFigure src="on-stage-4.jpg" >}}
{{< attachedFigure src="on-stage-5.jpg" >}}
![](on-stage-1.jpg)
![](on-stage-2.jpg)
![](on-stage-4.jpg)
![](on-stage-5.jpg)
## Attendees' reaction
{{< screenshotOf src="tweet-lbroudoux.png" href="https://twitter.com/lbroudoux/status/1119221025466720256" >}}
{{< screenshotOf src="tweet-gbloquel.png" href="https://twitter.com/gbloquel/status/1119170710696493056" >}}
{{< screenshotOf src="tweet-sebi2706.png" href="https://twitter.com/sebi2706/status/1119169813392314368" >}}
{{< screenshotOf src="tweet-cgodard.png" href="https://twitter.com/cgodard/status/1119159971835289600" >}}
{{< screenshotOf src="tweet-PetitMel_issa.png" href="https://twitter.com/PetitMel_issa/status/1119170065289678848" >}}
[![Tweet by @lbroudoux](tweet-lbroudoux.png)](https://twitter.com/lbroudoux/status/1119221025466720256)
[![Tweet by @gbloquel](tweet-gbloquel.png)](https://twitter.com/gbloquel/status/1119170710696493056)
[![Tweet by @sebi2706](tweet-sebi2706.png)](https://twitter.com/sebi2706/status/1119169813392314368)
[![Tweet by @cgodard](tweet-cgodard.png)](https://twitter.com/cgodard/status/1119159971835289600)
[![Tweet by @PetitMel_issa](tweet-PetitMel_issa.png)](https://twitter.com/PetitMel_issa/status/1119170065289678848)
For more information: https://cfp.devoxx.fr/2019/talk/OWQ-3436/Une_API,_de_l'idee_a_la_production,_en_mode_agile_avec_Red_Hat_!

2
content/english/speaking/red-hat-forum-2017/index.md

@ -11,6 +11,6 @@ _"Gestion des API dans une démarche CI/CD"_.
For more information: https://www.redhat.com/fr/events/red-hat-forum-france-2017
{{< screenshotOf src="tweet-ylebihan.png" href="https://twitter.com/ylebihan/status/918126983963447296" >}}
[![Tweet by @ylebihan](tweet-ylebihan.png)](https://twitter.com/ylebihan/status/918126983963447296)
[Slide deck can be found here](http://images.engage.redhat.com/Web/RedHat/%7Bd7510765-62e8-4ea8-9f64-226259730a17%7D_Gestion_des_API_dans_une_de%CC%81marche_CI_CD.pdf)

10
content/english/speaking/red-hat-open-demo-build-multi-architecture-ci-cd-pipelines-to-run-your-apps-in-the-cloud-and-at-the-edge/index.md

@ -21,7 +21,7 @@ On September 5, 2024, I presented a webinar named [Red Hat Open Demo-Build multi
In the rapidly advancing field of edge computing, deploying applications across diverse hardware platforms, such as ARM and x86_64, has become essential.
Multi-architecture container images have emerged as a powerful solution, supporting multiple processor architectures within a single image package and simplifying the deployment process across platforms.
{{< attachedFigure src="slide-arm-devices.png" >}}
![](slide-arm-devices.png)
During this demonstration, I explored how these multi-architecture images work seamlessly across different CPU architectures, automatically selecting the appropriate client architecture from a registry.
Using tools like Podman, Buildah, and Tekton, I showcased how easy it is to build these images.
@ -40,7 +40,7 @@ Here’s what was covered during the demo:
- **Tekton Task Binding to the Right Node**: I also demonstrated how Tekton tasks could be directed to specific nodes based on architecture, ensuring efficient execution across mixed environments.
{{< attachedFigure src="slide-architecture.png" >}}
![](slide-architecture.png)
Key Highlights from the Demo:
@ -50,11 +50,11 @@ Key Highlights from the Demo:
3. **Building and Pushing Multi-Architecture Container Images to quay.io**: I concluded with a real-time demonstration of building and pushing multi-architecture images to the quay.io registry, emphasizing the role of registry support in deploying cross-platform applications efficiently.
4. **Running the same container on two different CPU architectures**: I ran a container image built with the multi-architecture pipeline on both my Laptop (x86_64) and {{< internalLink path="/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md" title="my Ampere Altra server" >}} (ARM64).
4. **Running the same container on two different CPU architectures**: I ran a container image built with the multi-architecture pipeline on both my Laptop (x86_64) and [my Ampere Altra server](/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md) (ARM64).
This demo has been designed for DevOps professionals, cloud architects, and developers looking to leverage OpenShift and AWS in multi-architecture container image creation.
The session provided them with both a high-level understanding and practical skills to implement and manage these capabilities in their environments.
If you have not been able to attend the live session, I invite you to [watch the replay](https://events.redhat.com/profile/form/index.cfm?PKformID=0x11759490001) and [download the slides]({{< attachedFileLink src="slides.pdf" >}})!
If you have not been able to attend the live session, I invite you to [watch the replay](https://events.redhat.com/profile/form/index.cfm?PKformID=0x11759490001) and [download the slides](slides.pdf)!
If you are ready to dive deeper, have a look at the article I wrote on this subject: {{< internalLink path="/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md" >}}!
If you are ready to dive deeper, have a look at the article I wrote on this subject: [](/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md)!

10
content/english/speaking/red-hat-open-demo-from-the-esp8266-to-the-openshift-cluster-discover-what-red-hat-can-offer-for-the-edge-computing/index.md

@ -15,7 +15,7 @@ topics:
---
On April 4, 2023, I presented a webinar named [From the ESP8266 to the OpenShift cluster: discover what Red Hat can offer for the Edge computing!](https://events.redhat.com/profile/form/index.cfm?PKformID=0x1270056abcd&extIdCarryOver=true&sc_cid=701f2000000txokAAA) with three colleagues: [Adrien](https://www.linkedin.com/in/adrien-legros-78674a133/), [Florian](https://www.linkedin.com/in/florian-even/) and [Sébastien](https://www.linkedin.com/in/sebastien-lallemand/).
This webinar showcases a lab that we built and delivered for the {{< internalLink path="/speaking/red-hat-tech-exchange-2023/index.md" >}}.
This webinar showcases a lab that we built and delivered for the [](/speaking/red-hat-tech-exchange-2023/index.md).
<!--more-->
@ -26,13 +26,13 @@ The exercises revolved around the common and relatable theme of "shipment tracki
Participants helped a fictious company, FSC (the **F**edora **S**hipping **C**ompany), set up its Edge computing infrastructure to support 10 warehouses and a central headquarter across EMEA.
{{< attachedFigure src="slide-context.png" >}}
![](slide-context.png)
During the lab, attendees set up and deployed all essential components to create a connected parcel tracking system.
They began by configuring devices, using the Arduino IDE and an [ESP8266](https://en.wikipedia.org/wiki/ESP8266), to read the parcel tracking number using RFIDs, which is then transmitted over Wi-Fi to an MQTT broker.
Using Camel-K, the data was transformed into actionable events and sent to a central headquarter system for real-time reporting.
{{< attachedFigure src="architecture.svg" >}}
![](architecture.svg)
Meanwhile, at the headquarter level, a dedicated application visually mapped the movement of parcels as they traveled between hubs, creating an interactive, dynamic view of the entire shipment process.
@ -44,7 +44,7 @@ To make the lab as much fun and interactive as possible, the participants had to
- Two people, *ditto*, were setting up the firmware of the ESP8266 microcontrollers to scan **outgoing** parcels, along with the MQTT broker and the **outgoing** topic.
- Two people, *ditto*, were setting up the Camel-K routes and the Kafka broker.
{{< attachedFigure src="slide-principle.png" >}}
![](slide-principle.png)
Then, once everything properly setup, a participant scan a parcel in the **Athens** warehouse and then in the **Paris** warehouse and in realtime a parcel would move from **Athens** to **Paris** on the map displayed on the screen.
@ -65,7 +65,7 @@ Rince and repeat for the 25 units!
For each unit, it took me from one hour down to 20 minutes once properly trained.
Quite a time consuming task!
{{< attachedFigure src="esp8266-rc522.jpeg" >}}
![](esp8266-rc522.jpeg)
## Replay

14
content/english/speaking/red-hat-open-demo-mission-impossible-stop-the-crazy-train-with-ai-and-edge-before-it-is-too-late/index.md

@ -23,16 +23,16 @@ In this article, I will give you an overview of the demo and you will be able to
## The "Mission impossible" demo
We designed this demo for the {{< internalLink path="/speaking/platform-day-2024/index.md" >}} event based on the latest opus of the movie **Mission Impossible: Dead Reckoning**.
We designed this demo for the [](/speaking/platform-day-2024/index.md) event based on the latest opus of the movie **Mission Impossible: Dead Reckoning**.
In this demo, **Ethan Hunt** needs help to stop the **Lego City #60337** train before it's too late!
Nothing less than the fate of humanity is at stake!
{{< attachedFigure src="mission-impossible-plot.png" >}}
![](mission-impossible-plot.png)
The scenario requires **Ethan Hunt** to board the train to connect a **Nvidia Jetson Orin Nano** card to the train's computer network and deploy an AI that will recognise the traffic signs and stop the train on time before it derails!
A console will provide a remote view of the train's video surveillance camera, with the results of the AI model's inference overlaid.
{{< attachedFigure src="mission-impossible-scenario.png" >}}
![](mission-impossible-scenario.png)
To run this demo, we equipped the **Lego** train with a **Nvidia Jetson Orin Nano** card, a webcam and a portable battery.
The Nvidia Jetson Orin card is a System On Chip (SoC), it includes all the hardware that **Ethan Hunt** needs for its mission: CPU, RAM, storage...
@ -40,7 +40,7 @@ Plus a GPU to speed up the calculations!
The Jetson receives the video stream from the onboard camera and transmits orders to the **Lego** Hub via the **Bluetooth Low Energy** protocol.
It is powered by a portable battery for the duration of the mission.
{{< attachedFigure src="rhel-booth-mission-impossible-demo.jpeg" >}}
![](rhel-booth-mission-impossible-demo.jpeg)
We are in an Edge Computing context.
On the Jetson, we have installed **Red Hat Device Edge**.
@ -55,7 +55,7 @@ The video stream is broadcast from the Jetson via a **Kafka broker**!
On top of this, there are MLops pipelines to train the AI model.
And finally CI/CD pipelines to build the container images of our microservices for x86 and ARM architectures.
{{< attachedFigure src="mission-impossible-hardware-architecture.png" >}}
![](mission-impossible-hardware-architecture.png)
To enable **Ethan Hunt** to carry out its mission successfully, we had to guarantee end-to-end data transmission.
To do this, we implemented five services that communicate via an asynchronous message transmission system (**MQTT**).
@ -65,7 +65,7 @@ Each image is resized to 600x400 pixels and encapsulated in an event with a uniq
This event is transmitted to the AI model, which enriches it with the result of the prediction.
The latter is transmitted to a transformation service whose role is to extract the train's action, transmit it to the train controller to slow down or stop the train and at the same time send the event to the streaming service (**Kafka**) deployed on a remote Openshift, which displays the images and the prediction in real time.
{{< attachedFigure src="mission-impossible-software-architecture.png" >}}
![](mission-impossible-software-architecture.png)
And finally, we had to build an artificial intelligence model.
To do this, we followed best practices for managing the model's lifecycle, known as **MLOps**:
@ -78,7 +78,7 @@ To do this, we followed best practices for managing the model's lifecycle, known
- **Measure performance and re-train**: By observing the model's behaviour, we were able to measure the quality of the predictions and note that not all **Lego** panels were well recognised.
We decided to re-train the model by refining it with an enriched dataset.
{{< attachedFigure src="mission-impossible-ai.png" >}}
![](mission-impossible-ai.png)
## Watch the replay!

26
content/english/speaking/red-hat-summit-connect-france-2024/index.md

@ -33,7 +33,7 @@ All while immersing them in a captivating adventure where every exercise involve
During the workshop, participants had to develop no less than four microservices to build an application simulating combat between superheroes and super villains.
{{< attachedFigure src="open-code-quest-microservices.png" >}}
![](open-code-quest-microservices.png)
The microservices were developed in Quarkus, the native Java framework for the cloud, demonstrating how it can transform application development by combining speed of development, lightness and performance.
In particular, Quarkus significantly reduces the memory footprint of applications, while enabling them to start up almost instantaneously.
@ -46,7 +46,7 @@ Based on the **Tekton Chains** and **Sigstore** technologies, this product offer
Attendees were able to discover how these tools enhance the security of deployments by providing proof of compliance and transparency on the dependencies used in applications.
I'll let you discover the full list of tools used in the **Open Code Quest** workshop:
{{< attachedFigure src="open-code-quest-namespaces.png" >}}
![](open-code-quest-namespaces.png)
Before and during the **Open Code Quest**, the administration of the **platform** played a key role in the success of the event.
As an organising member, I was responsible, along with [Sébastien Lallemand](https://sebastienlallemand.net/), for preparing, sizing, installing and configuring the eight OpenShift clusters needed for the workshops to run smoothly.
@ -55,7 +55,7 @@ This crucial preparation phase ensured a stable, high-performance infrastructure
During the event, my role as SRE (Site Reliability Engineer) was to closely monitor critical metrics, such as resource utilisation, to ensure a smooth and optimal experience for all participants.
Thanks to this proactive monitoring, we were able to offer constant availability of the environments and thus facilitate the smooth running of the workshop.
{{< attachedFigure src="open-code-quest-clusters.png" >}}
![](open-code-quest-clusters.png)
Another challenge I tackled for the Open Code Quest was the creation of a **Leaderboard** designed to encourage emulation between participants.
This project required me to think outside the box, as I had to use tools such as **Prometheus** and **Grafana** for a task they weren't designed for: sorting participants by order of finish.
@ -76,22 +76,22 @@ How have I calibrated the bonuses and accelerators to encourage competition and
Everything is explained in these two articles:
1. {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}
2. {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md" >}}
1. [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md)
2. [](/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md)
## "Mission Impossible" demo: Lego, AI & Edge Computing
For part of the day, I was on the RHEL booth, accompanied by [Adrien](https://www.linkedin.com/in/adrien-legros-78674a133/), [Mourad](https://www.linkedin.com/in/mourad-ouachani-0734218/) and [Pauline](https://www.linkedin.com/in/trg-pauline/) to install the "Mission Impossible" demo and answer questions from the public.
We designed this demo for the {{< internalLink path="/speaking/platform-day-2024/index.md" >}} event based on the latest opus of the movie **Mission Impossible: Dead Reckoning**.
We designed this demo for the [](/speaking/platform-day-2024/index.md) event based on the latest opus of the movie **Mission Impossible: Dead Reckoning**.
In this demo, **Ethan Hunt** needs help to stop the **Lego City #60337** train before it's too late!
Nothing less than the fate of humanity is at stake!
{{< attachedFigure src="mission-impossible-plot.png" >}}
![](mission-impossible-plot.png)
The scenario requires **Ethan Hunt** to board the train to connect a **Nvidia Jetson Orin Nano** card to the train's computer network and deploy an AI that will recognise the traffic signs and stop the train on time before it derails!
A console will provide a remote view of the train's video surveillance camera, with the results of the AI model's inference overlaid.
{{< attachedFigure src="mission-impossible-scenario.png" >}}
![](mission-impossible-scenario.png)
To run this demo, we equipped the **Lego** train with a **Nvidia Jetson Orin Nano** card, a webcam and a portable battery.
The Nvidia Jetson Orin card is a System On Chip (SoC), it includes all the hardware that **Ethan Hunt** needs for its mission: CPU, RAM, storage...
@ -99,7 +99,7 @@ Plus a GPU to speed up the calculations!
The Jetson receives the video stream from the onboard camera and transmits orders to the **Lego** Hub via the **Bluetooth Low Energy** protocol.
It is powered by a portable battery for the duration of the mission.
{{< attachedFigure src="rhel-booth-mission-impossible-demo.jpeg" >}}
![](rhel-booth-mission-impossible-demo.jpeg)
We are in an Edge Computing context.
On the Jetson, we have installed **Red Hat Device Edge**.
@ -114,7 +114,7 @@ The video stream is broadcast from the Jetson via a **Kafka broker**!
On top of this, there are MLops pipelines to train the AI model.
And finally CI/CD pipelines to build the container images of our microservices for x86 and ARM architectures.
{{< attachedFigure src="mission-impossible-hardware-architecture.png" >}}
![](mission-impossible-hardware-architecture.png)
To enable **Ethan Hunt** to carry out its mission successfully, we had to guarantee end-to-end data transmission.
To do this, we implemented five services that communicate via an asynchronous message transmission system (**MQTT**).
@ -124,7 +124,7 @@ Each image is resized to 600x400 pixels and encapsulated in an event with a uniq
This event is transmitted to the AI model, which enriches it with the result of the prediction.
The latter is transmitted to a transformation service whose role is to extract the train's action, transmit it to the train controller to slow down or stop the train and at the same time send the event to the streaming service (**Kafka**) deployed on a remote Openshift, which displays the images and the prediction in real time.
{{< attachedFigure src="mission-impossible-software-architecture.png" >}}
![](mission-impossible-software-architecture.png)
And finally, we had to build an artificial intelligence model.
To do this, we followed best practices for managing the model's lifecycle, known as **MLOps**:
@ -137,9 +137,9 @@ To do this, we followed best practices for managing the model's lifecycle, known
- **Measure performance and re-train**: By observing the model's behaviour, we were able to measure the quality of the predictions and note that not all **Lego** panels were well recognised.
We decided to re-train the model by refining it with an enriched dataset.
{{< attachedFigure src="mission-impossible-ai.png" >}}
![](mission-impossible-ai.png)
If you weren't able to come and see us on the stand, you can catch up with us in the video below (captured during the {{< internalLink path="/speaking/platform-day-2024/index.md" >}}).
If you weren't able to come and see us on the stand, you can catch up with us in the video below (captured during the [](/speaking/platform-day-2024/index.md)).
You can see the train stop when it detects the corresponding road sign.
{{< embeddedVideo src="mission-impossible-demo.mp4" autoplay="true" loop="true" muted="true" width="1920" height="1080" >}}

3
content/english/speaking/red-hat-tech-exchange-2018/index.md

@ -14,4 +14,5 @@ From the 17th to 21th Septembre 2018, I co-presented two sessions:
For the session _An API Journey: from mock to deployment_, we received an award
based on the amazing feedbacks from the public!
{{< attachedFigure src="2018-10-21-RHTE-Award.jpg" title="We received our award, on-stage!" >}}
![We received our award, on-stage!](2018-10-21-RHTE-Award.jpg "We received our award, on-stage!")

3
content/english/speaking/red-hat-tech-exchange-2019/index.md

@ -13,4 +13,5 @@ During the three Red Hat Tech Exchange sessions (Americas, EMEA and APAC), I pre
The session _Leverage the power of open source communities to manage your APIs_ has been the second best voted session in RHTE APAC!
{{< attachedFigure src="2019-10-21-RHTE-Award.png" title="I received an award!" >}}
![I received an award!](2019-10-21-RHTE-Award.png "I received an award!")

2
content/english/speaking/red-hat-tech-exchange-2023/index.md

@ -22,7 +22,7 @@ An application at the headquarter displays the parcels moving from one hub to an
The whole room had a lot of fun!
{{< attachedFigure src="Image_20230203_090806_445.jpeg" title="I presented the Lab organization." >}}
![I presented the Lab organization.](Image_20230203_090806_445.jpeg "I presented the Lab organization.")
If you want to play with the Lab, all the code is under the [RHTE-2023-Edge-Lab](https://github.com/RHTE-2023-Edge-Lab) organization.

2
content/english/speaking/techweek-sg-2019/index.md

@ -8,7 +8,7 @@ draft: false
On the 12th November 2019, I spoke at Société Générale for their TechWeek event on a session named _"Accelerate and secure your API releases with Microcks"_.
{{< screenshotOf src="tweet-YadaYac.png" href="https://twitter.com/YadaYac/status/1194254972872986624" >}}
[![Tweet by @YadaYac](tweet-YadaYac.png)](https://twitter.com/YadaYac/status/1194254972872986624)
The session was a one hour lab focusing on the design, mock and tests of REST APIs.
The [lab documents](https://microcks-workshop.netlify.com/) and [source code](https://github.com/nmasse-itix/microcks-workshop) are freely available!

2
content/english/writing/apis-as-a-product-get-started-in-no-time.md

@ -8,6 +8,6 @@ topics:
- API Management
---
In the previous article, [APIs as a Product: Get the value out of your APIs]({{< relref "apis-as-a-product-get-the-value-out-of-your-apis.md" >}}), we presented a new approach called “APIs as a Product” to maximize the value of your APIs. In this article, we show how to quickly get started with APIs as a Product using the new features of Red Hat 3scale API Management 2.7.
In the previous article, [APIs as a Product: Get the value out of your APIs](apis-as-a-product-get-the-value-out-of-your-apis.md), we presented a new approach called “APIs as a Product” to maximize the value of your APIs. In this article, we show how to quickly get started with APIs as a Product using the new features of Red Hat 3scale API Management 2.7.
[Continue reading](https://developers.redhat.com/blog/2019/12/03/apis-as-a-product-get-started-in-no-time/)

8
content/french/_index.md

@ -3,9 +3,9 @@ title: "Nicolas Massé"
---
Je suis Nicolas Massé, vous me trouverez sur les réseaux sociaux sous le pseudo "nmasse-itix".
Développeur de formation, mon métier actuel est consultant avant-vente dans les domaines de l'[API Management]({{<relref "/topics/api-management" >}}), le [Single Sign On]({{<relref "/topics/openid-connect" >}}) et les [conteneurs]({{<relref "/topics/containers" >}}).
Développeur de formation, mon métier actuel est consultant avant-vente dans les domaines de l'[API Management](/topics/api-management), le [Single Sign On](/topics/openid-connect) et les [conteneurs](/topics/containers).
Je travaille principalement avec des technologies [Open Source]({{<relref "/opensource" >}}) telles que [Keycloak]({{<relref "/opensource/keycloak" >}}), [3scale]({{<relref "/opensource/3scale" >}}) ou [OpenShift]({{<relref "/opensource/openshift" >}}).
Je partage mon [expertise]({{<relref "/topics" >}}) et mes centres d'intérêt sur ce site, [ailleurs sur le web]({{<relref "/writing" >}}) et je donne occasionnellement des [conférences]({{<relref "/speaking" >}}).
Je travaille principalement avec des technologies [Open Source](/opensource) telles que [Keycloak](/opensource/keycloak), [3scale](/opensource/3scale) ou [OpenShift](/opensource/openshift).
Je partage mon [expertise](/topics) et mes centres d'intérêt sur ce site, [ailleurs sur le web](/writing) et je donne occasionnellement des [conférences](/speaking).
Je vous invite à lire [mon blog]({{<relref "/blog" >}}) ou à en savoir plus [sur moi](https://www.linkedin.com/in/nicolasmasse/).
Je vous invite à lire [mon blog](/blog) ou à en savoir plus [sur moi](https://www.linkedin.com/in/nicolasmasse/).

28
content/french/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md

@ -16,7 +16,7 @@ resources:
- '*.gif'
---
Lors du {{< internalLink path="/speaking/red-hat-summit-connect-france-2024/index.md" >}}, j'ai animé un atelier pour les développeurs intitulé "**Open Code Quest**".
Lors du [](/speaking/red-hat-summit-connect-france-2024/index.md), j'ai animé un atelier pour les développeurs intitulé "**Open Code Quest**".
Dans cet atelier, les développeurs devaient coder des micro-services en utilisant Quarkus, OpenShift et un service d'Intelligence Artificielle : le modèle Granite d'IBM.
L'atelier était conçu sous la forme d'une compétition de vitesse : les premiers à valider les trois exercices ont reçu une récompense.
@ -75,7 +75,7 @@ Et c'est justement ce qui nous intéresse :
Et si les trois conditions précédentes sont vraies, on peut en déduire que l'utilisateur a terminé et validé l'exercice **hero**.
Au cours du temps, ces *time series* progressent telles que représentées sur la figure suivante.
{{< attachedFigure src="exercise-validation.png" title="Lorsque les trois conditions sont réunies, l'exercice est validé." >}}
![Lorsque les trois conditions sont réunies, l'exercice est validé.](exercise-validation.png "Lorsque les trois conditions sont réunies, l'exercice est validé.")
C'est un bon début, non ?
Si on fait la même chose pour les trois exercices, on peut savoir qui a terminé l'atelier dans son ensemble.
@ -84,7 +84,7 @@ Vu que certains exercices prennent plus de temps que d'autres, on peut imaginer
C'est ce que j'ai essayé de modéliser dans la figure ci-dessous avec un poids de 55 pour le premier exercice, 30 pour le second et 45 pour le dernier.
L'idée étant d'approcher une progression linéaire des points au cours du temps (1 point par minute).
{{< attachedFigure src="counting-scheme-no-time.png" title="Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec pondération de chaque exercise en fonction de la durée nominale de l'exercise." >}}
![Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec pondération de chaque exercise en fonction de la durée nominale de l'exercise.](counting-scheme-no-time.png "Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec pondération de chaque exercise en fonction de la durée nominale de l'exercise.")
Ça commence à prendre forme.
Mais si on regarde bien, à la fin de l'atelier (à la 150ème minute), tous les participants ont terminé et ont le même score.
@ -108,12 +108,12 @@ Est-ce qu'on ne pourrait pas activer un accélérateur à chaque validation d'un
Voilà qui rendrait la compétition plus engageante et plus amusante !
Et c'est ce que j'ai essayé de modéliser sur le schéma ci-dessous.
{{< attachedFigure src="counting-scheme-with-time.png" title="Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec accélérateur et pondération de chaque exercise en fonction du temps que met l'utilisateur à réaliser l'exercice." >}}
![Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec accélérateur et pondération de chaque exercise en fonction du temps que met l'utilisateur à réaliser l'exercice.](counting-scheme-with-time.png "Progression du nombre de points pour un utilisateur normal, lent et rapide au cours du temps et avec accélérateur et pondération de chaque exercise en fonction du temps que met l'utilisateur à réaliser l'exercice.")
Maintenant, la question est : est-ce qu'un utilisateur qui prend la tête dans le premier exercice acquiert un avantage significatif qui rendrait la compétition déséquilibrée ?
La réponse, nous l'avons obtenue lors des différentes répétitions qui ont eu lieu chez Red Hat avant le Jour J.
{{< attachedFigure src="counting-scheme-dry-run.png" title="Validation du modèle de comptage des points lors d'un dry-run." >}}
![Validation du modèle de comptage des points lors d'un dry-run.](counting-scheme-dry-run.png "Validation du modèle de comptage des points lors d'un dry-run.")
Dans la capture d'écran ci-dessus, on voit que Batman a terminé l'exercice "hero" **tardivement**.
Mais en terminant l'exercice "villain" **très rapidement**, il a pu reprendre la tếte... **temporairement**.
@ -169,7 +169,7 @@ Des données doivent normalement apparaître dans le tableau de bord Grafana.
Pour en profiter pleinement, arrêtez le script `run.sh` avec un appui sur **Ctrl + C** et relancez le !
Au bout de quelques secondes, vous devriez voir apparaitre sur le tableau de bord des données toutes fraiches, comme dans la vidéo ci-dessous.
{{< attachedFigure src="leaderboard-simulation.gif" title="Simulation de l'atelier Open Code Quest sur le banc d'essai afin de valider le système de comptage de points (vidéo accélérée 10x)." >}}
![Simulation de l'atelier Open Code Quest sur le banc d'essai afin de valider le système de comptage de points (vidéo accélérée 10x).](leaderboard-simulation.gif "Simulation de l'atelier Open Code Quest sur le banc d'essai afin de valider le système de comptage de points (vidéo accélérée 10x).")
### Requêtes Prometheus
@ -227,7 +227,7 @@ EOF
Copiez-collez la requète dans la section **Explore** de Grafana et vous devriez obtenir le graphe suivant.
{{< attachedFigure src="grafana-explore-opencodequest-leaderboard-hero.png" title="La métrique \"opencodequest_leaderboard_hero:prod\" représente l'état de complétude de l'exercice \"hero\" dans l'environnement \"prod\"." >}}
![La métrique "opencodequestleaderboardhero:prod" représente l'état de complétude de l'exercice "hero" dans l'environnement "prod".](grafana-explore-opencodequest-leaderboard-hero.png "La métrique \"opencodequest_leaderboard_hero:prod\" représente l'état de complétude de l'exercice \"hero\" dans l'environnement \"prod\".")
Il faut le lire de la manière suivante (note : 1728646377 = 13:32:57) :
@ -290,7 +290,7 @@ Ces trois requêtes sont conçues sur le même modèle.
Pour bien comprendre comment fonctionne cette requête, je vous propose de la scinder en deux : la partie `increase(...)` d'un coté et le reste de l'autre.
On superpose tout ça avec la requête précédente et ça donne la figure suivante.
{{< attachedFigure src="grafana-opencodequest-leaderboard-onetime-bonus.png" title="La métrique \"opencodequest_leaderboard_hero_onetime_bonus:prod\" représente le bonus temps alloué à un utilisateur lorsqu'il termine l'exercice \"hero\" dans l'environnement \"prod\"." >}}
![La métrique "opencodequestleaderboardheroonetimebonus:prod" représente le bonus temps alloué à un utilisateur lorsqu'il termine l'exercice "hero" dans l'environnement "prod".](grafana-opencodequest-leaderboard-onetime-bonus.png "La métrique \"opencodequest_leaderboard_hero_onetime_bonus:prod\" représente le bonus temps alloué à un utilisateur lorsqu'il termine l'exercice \"hero\" dans l'environnement \"prod\".")
De haut en bas, on peut observer :
@ -320,7 +320,7 @@ On peut le voir comme l'intégrale de la *time serie*.
La figure suivante présente le fonctionnement de cette requête de manière plus parlante.
{{< attachedFigure src="grafana-opencodequest-leaderboard-lifetime-bonus.png" title="La métrique \"opencodequest_leaderboard_hero_lifetime_bonus:prod\" représente le report à nouveau du bonus temps alloué à un utilisateur lorsqu'il termine l'exercice \"hero\" dans l'environnement \"prod\"." >}}
![La métrique "opencodequestleaderboardherolifetimebonus:prod" représente le report à nouveau du bonus temps alloué à un utilisateur lorsqu'il termine l'exercice "hero" dans l'environnement "prod".](grafana-opencodequest-leaderboard-lifetime-bonus.png "La métrique \"opencodequest_leaderboard_hero_lifetime_bonus:prod\" représente le report à nouveau du bonus temps alloué à un utilisateur lorsqu'il termine l'exercice \"hero\" dans l'environnement \"prod\".")
De haut en bas, on peut observer :
@ -366,7 +366,7 @@ En effet, les 3 accélérateurs s'additionnent déjà, ce qui fait que l'utilisa
La figure suivante présente les 6 composantes de requête Prometheus permettant de calculer les points de l'utilisateur.
{{< attachedFigure src="grafana-opencodequest-leaderboard.png" title="Les 6 composantes de la requête Prometheus calculant les scores des utilisateurs et le résultat." >}}
![Les 6 composantes de la requête Prometheus calculant les scores des utilisateurs et le résultat.](grafana-opencodequest-leaderboard.png "Les 6 composantes de la requête Prometheus calculant les scores des utilisateurs et le résultat.")
### *Recording Rules*
@ -440,7 +440,7 @@ La syntaxe `${user:regex}` permet à Grafana de remplacer `user=~"${user:regex}"
Pour montrer le classement instantané, j'ai utilisé la visualisation **Bar Chart** avec une transformation de type **Sort by** sur le champ **Value**.
{{< attachedFigure src="grafana-opencodequest-leaderboard-instant-snapshot.png" title="Paramètres de la visualisation Grafana pour le classement instantané." >}}
![Paramètres de la visualisation Grafana pour le classement instantané.](grafana-opencodequest-leaderboard-instant-snapshot.png "Paramètres de la visualisation Grafana pour le classement instantané.")
Les paramètres importants de cette visualisation sont :
@ -452,7 +452,7 @@ Les paramètres importants de cette visualisation sont :
Pour suivre la progression des points au cours du temps, j'ai opté pour la visualisation **Time series**.
{{< attachedFigure src="grafana-opencodequest-leaderboard-points-over-time.png" title="Paramètres de la visualisation Grafana pour la progression des points." >}}
![Paramètres de la visualisation Grafana pour la progression des points.](grafana-opencodequest-leaderboard-points-over-time.png "Paramètres de la visualisation Grafana pour la progression des points.")
Les paramètres importants de cette visualisation sont :
@ -478,7 +478,7 @@ Quant à la question qui est sur toutes les lèvres : est-ce qu'il y a eu de la
La réponse est un grand **OUI** !
Et il y a eu du frisson lors de l'annonce des résultats...
{{< attachedFigure src="grafana-opencodequest-points.png" title="Progression des points des 74 participants lors de l'Open Code Quest." >}}
![Progression des points des 74 participants lors de l'Open Code Quest.](grafana-opencodequest-points.png "Progression des points des 74 participants lors de l'Open Code Quest.")
Observez toutes ces courbes qui se croisent, tous ces super-héros en compétition pour la première place !
@ -490,4 +490,4 @@ Ce projet a non seulement mis en lumière des technologies comme Quarkus, OpenSh
Concevoir le Leaderboard, bien que complexe, a ajouté une dimension compétitive motivante à l’atelier.
Le jour J, voir les participants rivaliser de rapidité tout en explorant les solutions Red Hat a été incroyablement gratifiant.
Et pour savoir comment j'ai implémenté ce Leaderboard dans une architecture multi-cluster avec Red Hat ACM, c'est par ici : {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md" >}}.
Et pour savoir comment j'ai implémenté ce Leaderboard dans une architecture multi-cluster avec Red Hat ACM, c'est par ici : [](/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md).

8
content/french/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md

@ -17,7 +17,7 @@ resources:
- '*.gif'
---
Après avoir révélé les coulisses de la conception du Leaderboard pour l'atelier "Open Code Quest" lors du {{< internalLink path="/speaking/red-hat-summit-connect-france-2024/index.md" >}}, il est temps de plonger plus en détail dans son implémentation pratique !
Après avoir révélé les coulisses de la conception du Leaderboard pour l'atelier "Open Code Quest" lors du [](/speaking/red-hat-summit-connect-france-2024/index.md), il est temps de plonger plus en détail dans son implémentation pratique !
Dans cet article, je vais vous guider à travers la configuration de **Red Hat Advanced Cluster Management** ainsi que les différentes adapatations nécessaires pour connecter le *Leaderboard* créé précédemment avec l'infrastructure de l'**Open Code Quest**.
@ -25,7 +25,7 @@ Embarquez avec moi pour cette nouvelle étape, plus technique que la précédent
<!--more-->
Cet article est la suite de {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}.
Cet article est la suite de [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md).
Si vous ne l'avez pas lu, je vous conseille de le lire avant pour mieux comprendre le contexte.
## Requêtes Prometheus
@ -227,7 +227,7 @@ L'observabilité est un module supplémentaire (dans le sens où il n'est pas in
Le schéma suivant présente l'architecture du module d'observabilité dans **Red Hat Advanced Cluster Management**.
Je l'ai créé en observant les relations entre les composants à partir d'une installation d'ACM en version 2.11.
{{< attachedFigure src="redhat-acm-observability-architecture.svg" title="Architecture logique de l'observabilité dans Red Hat Advanced Cluster Management 2.11" >}}
![Architecture logique de l'observabilité dans Red Hat Advanced Cluster Management 2.11](redhat-acm-observability-architecture.svg "Architecture logique de l'observabilité dans Red Hat Advanced Cluster Management 2.11")
Les composants déployés sur le cluster central sont en **vert**, ceux déployés sur les clusters managés sont en **bleu** et les éléments de configuration en **gris**.
J'ai aussi illustré les deux endroits possibles pour le calcul des *recording rules*, en **jaune**.
@ -430,7 +430,7 @@ read -q "?press any key to continue "
./switch-to-grafana-admin.sh "$(oc whoami)"
```
Puis, créer le tableau de bord "Red Hat Summit Connect 2024", comme expliqué dans l'article {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}.
Puis, créer le tableau de bord "Red Hat Summit Connect 2024", comme expliqué dans l'article [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md).
Et enfin, exporter le tableau de bord sous la forme d'une ConfigMap.

34
content/french/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md

@ -15,7 +15,7 @@ resources:
- '*.svg'
---
En 2022, j'ai écrit un article sur ce sujet intitulé {{< internalLink path="/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md" >}}.
En 2022, j'ai écrit un article sur ce sujet intitulé [](/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-and-qemu/index.md).
L'article relatait la configuration que j'avais mise en place pour mes projets personnels.
Il a même un peu débordé de sa vocation initiale puisqu'il a également été utilisé par plusieurs collègues chez Red Hat qui avaient le même besoin.
Si la configuration décrite dans ce précédent article est toujours d'actualité, l'approche est un peu datée.
@ -56,7 +56,7 @@ Si le précédent article ciblait les plateformes Kubernetes *vanilla*, dans cel
Dans les pipelines Tekton, j'utilise **Buildah** pour construire et pousser sur la *registry* les images de conteneurs.
{{< attachedFigure src="overview.svg" title="Vue d'ensemble de la configuration mise en oeuvre." >}}
![Vue d'ensemble de la configuration mise en oeuvre.](overview.svg "Vue d'ensemble de la configuration mise en oeuvre.")
## Déploiement d'OpenShift sur AWS en mode multi-architecture
@ -105,7 +105,7 @@ curl -sfL https://mirror.openshift.com/pub/openshift-v4/multi/clients/ocp/4.15.1
Créer le fichier **install-config.yaml**.
Les zones surlignées sont les points importants du fichier.
{{< highlightFile "install-config.yaml" "yaml" "hl_lines=10 15 26 29" >}}
```yaml {filename="install-config.yaml" hl_lines="10 15 26 29"}
additionalTrustBundlePolicy: Proxyonly
apiVersion: v1
baseDomain: aws.itix.cloud
@ -154,7 +154,7 @@ publish: External
pullSecret: 'REDACTED'
sshKey: |
REDACTED
{{< /highlightFile >}}
```
J'ai choisis de laisser la déclaration des *Compute nodes* mais avec un *replicas* à 0 car cela simplifie l'instanciation du *Compute node* **x86_64** par la suite.
@ -169,7 +169,7 @@ Comme la [documentation OpenShift 4.15](https://docs.openshift.com/container-pla
Editer le fichier **manifests/cvo-overrides.yaml** et ajouter la section **additionalEnabledCapabilities**.
Ne faites pas un copier-coller du fichier complet ci-dessous car le **clusterID** est spécifique à votre installation !
{{< highlightFile "manifests/cvo-overrides.yaml" "yaml" "hl_lines=10-21" >}}
```yaml {filename="manifests/cvo-overrides.yaml" hl_lines="10-21"}
apiVersion: config.openshift.io/v1
kind: ClusterVersion
metadata:
@ -191,7 +191,7 @@ spec:
- OperatorLifecycleManager
- Build
- DeploymentConfig
{{< /highlightFile >}}
```
Démarrer l'installation du cluster sur AWS.
@ -362,7 +362,7 @@ oc -n openshift-machine-api scale machineset build-multiarch-tw9w9-aarch64-worke
- 1 *Compute node* **x86_64**
- 1 *Compute node* **arm64**
{{< attachedFigure src="openshift-console-three-nodes-x86_64-and-arm64.png" title="Console OpenShift affichant trois noeuds : deux noeuds x86_64 et un arm64." >}}
![Console OpenShift affichant trois noeuds : deux noeuds x8664 et un arm64.](openshift-console-three-nodes-x86_64-and-arm64.png "Console OpenShift affichant trois noeuds : deux noeuds x86_64 et un arm64.")
## Installation de l'opérateur Tekton
@ -453,7 +453,7 @@ spec:
Créer un volume EFS en suivant les étapes décrites dans la [documentation AWS](https://docs.aws.amazon.com/efs/latest/ug/gs-step-two-create-efs-resources.html).
{{< attachedFigure src="aws-console-efs.png" title="Console AWS montrant un volume EFS." >}}
![Console AWS montrant un volume EFS.](aws-console-efs.png "Console AWS montrant un volume EFS.")
Créer la **StorageClass** associée au volume EFS.
Le champ **fileSystemId** correspond à la deuxième colonne de la capture d'écran ci-dessus.
@ -476,7 +476,7 @@ parameters:
En suivant la documentation [OpenShift 4.15](https://docs.openshift.com/container-platform/4.15/storage/container_storage_interface/persistent-storage-csi-aws-efs.html#efs-create-volume_persistent-storage-csi-aws-efs), modifier le **Security Group** EFS pour autoriser les noeuds OpenShift à accéder au stockage via le protocole NFS.
{{< attachedFigure src="aws-console-security-group.png" title="Console AWS montrant la configuration du Security Group EFS." >}}
![Console AWS montrant la configuration du Security Group EFS.](aws-console-security-group.png "Console AWS montrant la configuration du Security Group EFS.")
À ce stade, le service AWS EFS est accessible depuis le cluster OpenShift mais il n'est pas encore utilisable par les pipelines Tekton.
En effet, l'**affinity-assistant** de Tekton interdit à un volume persistant RWX d'être monté à deux endroits en même temps (erreur *"more than one PersistentVolumeClaim is bound"*).
@ -533,7 +533,7 @@ Les spécificités de cette tâche sont liées au pré-requis nécessaire à l'e
- La variable d'environnement `STORAGE_DRIVER=vfs` désactive l'utilisation de **overlayfs**.
- Enfin, la *capability* **SETFCAP** est affectée à **buildah**.
{{< highlightFile "tekton/task-buildah-build.yaml" "yaml" "hl_lines=19-20 25-26 33-35 37-38" >}}
```yaml {filename="tekton/task-buildah-build.yaml" hl_lines="19-20 25-26 33-35 37-38"}
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
@ -572,7 +572,7 @@ spec:
volumeMounts:
- name: container-storage
mountPath: /var/lib/containers
{{< /highlightFile >}}
```
Le script exécuté par cette tâche est concis (seulement deux étapes) :
@ -638,11 +638,11 @@ Le premier cas d'usage présenté au début de cet article porte sur l'utilisati
Le pipeline correspondant récupère le code source, exécute un **buildah build** pour chaque architecture (**x86_64** et **arm64**) et pousse le manifeste sur la *registry*.
{{< attachedFigure src="tekton-pipeline-containerfile.png" title="Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture (Containerfile construisant une image de conteneur de l'utilitaire `htop`)." >}}
![Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture (Containerfile construisant une image de conteneur de l'utilitaire htop).](tekton-pipeline-containerfile.png "Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture (Containerfile construisant une image de conteneur de l'utilitaire `htop`).")
L'image résultante sur la registry quay.io montre bien un manifeste contenant deux images : une pour **x86_64** et une pour **arm64**.
{{< attachedFigure src="quay-image-containerfile.png" title="Console Quay montrant une image multi-architecture de l'utilitaire `htop`)." >}}
![Console Quay montrant une image multi-architecture de l'utilitaire htop).](quay-image-containerfile.png "Console Quay montrant une image multi-architecture de l'utilitaire `htop`).")
### Pipeline "NodeJS"
@ -651,14 +651,14 @@ Le pipeline NodeJS est sensiblement différent dans le sens où le dépôt Git e
La raison est que la commande **npm install** va télécharger les dépendances de l'application et certaines de ces dépendances peuvent embarquer du code natif (c'est le cas de certaines bibliothèques bas niveau).
Il faut donc un *workspace* séparé pour chaque architecture.
{{< attachedFigure src="tekton-pipeline-nodejs.png" title="Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application NodeJS." >}}
![Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application NodeJS.](tekton-pipeline-nodejs.png "Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application NodeJS.")
### Pipeline "Quarkus"
Le pipeline Quarkus est encore différent du pipeline NodeJS dans le sens où l'application Quarkus est compilée en mode JVM et donc la compilation n'a lieu qu'une seule fois.
Le bytecode contenu dans les fichiers Jar pouvant tourner de manière indifférenciée sur n'importe quelle architecture, il n'y a qu'un workspace Git partagé entre toutes les architectures.
{{< attachedFigure src="tekton-pipeline-quarkus.png" title="Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application Quarkus." >}}
![Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application Quarkus.](tekton-pipeline-quarkus.png "Console OpenShift montrant l'exécution du pipeline Tekton multi-architecture construisant une application Quarkus.")
### Lier une tâche à une architecture
@ -666,7 +666,7 @@ Jusqu'à présent l'article ne dit pas comment Tekton s'organise pour exécuter
Il n'y a pas de magie ! Tout se passe dans le **PipelineRun** !
{{< highlightFile "examples/htop/tekton/pipelinerun.yaml" "yaml" "hl_lines=41-49" >}}
```yaml {filename="examples/htop/tekton/pipelinerun.yaml" hl_lines="41-49"}
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
@ -716,7 +716,7 @@ spec:
podTemplate:
nodeSelector:
beta.kubernetes.io/arch: arm64
{{< /highlightFile >}}
```
Le champ **taskRunSpecs** permet d'ajouter des contraintes d'exécution à une ou plusieurs tâches du pipeline.

12
content/french/blog/connect-podman-container-to-openvswitch/index.md

@ -64,10 +64,10 @@ ip -n podman-c1 link set ivs123 up
Pour tester facilement notre setup, je propose de créer une image de conteneur avec tout le nécessaire.
Pour cela, créer un fichier **Containerfile** avec le contenu suivant:
{{< highlightFile "Containerfile" "docker" "" >}}
```docker {filename="Containerfile"}
FROM quay.io/centos/centos:stream10
RUN dnf install -y iproute bind-utils net-tools iputils && dnf clean all
{{< / highlightFile >}}
```
Construire l'image de conteneur avec :
@ -116,7 +116,7 @@ ovs-vsctl add-br ivs
Créer le réseau libvirt qui référence le bridge **Open vSwitch**.
Pour cela, vous devrez créer le fichier de définition au format XML :
{{< highlightFile "ovs-net.xml" "xml" "" >}}
```xml {filename="ovs-net.xml"}
<network>
<name>ivs</name>
<forward mode="bridge" />
@ -126,7 +126,7 @@ Pour cela, vous devrez créer le fichier de définition au format XML :
<portgroup name='default' default='true'>
</portgroup>
</network>
{{< /highlightFile >}}
```
Puis créer le réseau libvirt depuis ce fichier de définition :
@ -250,7 +250,7 @@ Pour éviter de coder dans le Quadlet tous les paramètres réseau, je me suis a
Le quadlet résultat est celui-ci :
{{< highlightFile "/etc/containers/systemd/test-openvswitch.container" "ini" "" >}}
```ini {filename="/etc/containers/systemd/test-openvswitch.container"}
[Unit]
Description=Sample container to test the integration with Open vSwitch
Before=openvswitch.service
@ -282,7 +282,7 @@ ExecStopPost=/bin/sh -Eeuo pipefail -c 'if ip -n $NS -br addr show | grep -q "^$
[Install]
# Start by default on boot
WantedBy=multi-user.target default.target
{{< /highlightFile >}}
```
L'adresse mac statique du conteneur peut se définir au niveau de l'interface **Open vSwitch**.
Dans la commande suivante, les guillemets sont échappés car ils font partie de la valeur attendue par **Open vSwitch**.

26
content/french/blog/decouverte-de-coreos/index.md

@ -74,7 +74,7 @@ Vous remarquerez que le fichier [ignition](https://coreos.github.io/ignition/con
C'est également une spécificité de CoreOS : tous les logiciels qui tournent sur CoreOS doivent tourner sous la forme de conteneurs, instanciés et démarrés depuis un *unit* systemd ou provisionné par OpenShift.
Note: si vous souhaitez tout de même installer un package RPM, il est possible de personnaliser l'image CoreOS avec vos modification.
Je vous conseille alors la lecture de cet article : {{< internalLink path="/blog/build-your-own-distribution-on-fedora-coreos.md" >}}.
Je vous conseille alors la lecture de cet article : [](/blog/build-your-own-distribution-on-fedora-coreos.md).
Autre différence majeure avec un système d'exploitation classique : les mises à jour s'effectuent de manière transactionnelle.
Cela signifie qu'une mise à jour du système d'exploitation s'effectue dans son intégralité ou ne se fait pas du tout.
@ -133,7 +133,7 @@ Chaque version (identifiée par son empreinte SHA 256) contient l'ensemble du sy
La commande `ostree admin status` permet de lister les versions installées.
{{< highlight "sh" "hl_lines=2 14" >}}
```sh {hl_lines="2 14"}
[core@coreos ~]$ sudo ostree admin status
* rhcos eb6dd3b8b2912914f568af791a7ece826665cc78153a4e4d304acdaae1daacd1.0
Version: 48.84.202106231817-0
@ -150,7 +150,7 @@ boot etc lib media opt proc run srv sysroot usr
/ostree/deploy/rhcos/deploy/eb6dd3b8b2912914f568af791a7ece826665cc78153a4e4d304acdaae1daacd1.0/:
bin dev home lib64 mnt ostree root sbin sys tmp var
boot etc lib media opt proc run srv sysroot usr
{{< / highlight >}}
```
Pour éviter de gâcher de l'espace disque, les fichiers communs à deux versions sont partagés via un *hard link*.
Ici, la commande `ls` est identique entre les deux versions et ainsi les deux fichiers ont le même *inode*.
@ -191,7 +191,7 @@ Red Hat CoreOS est une version de Red Hat Enterprise Linux, packagée de manièr
Le premier indice se trouve dans le fichier /etc/os-release qui nous indique que la version de CoreOS embarquée avec OpenShift 4.8 est basée sur une Red Hat Enterprise Linux 8.4.
{{< highlightFile "/etc/os-release" "sh" "hl_lines=18" >}}
```sh {filename="/etc/os-release" hl_lines="18"}
NAME="Red Hat Enterprise Linux CoreOS"
VERSION="48.84.202108062347-0"
ID="rhcos"
@ -211,32 +211,32 @@ REDHAT_SUPPORT_PRODUCT_VERSION="4.8"
OPENSHIFT_VERSION="4.8"
RHEL_VERSION="8.4"
OSTREE_VERSION='48.84.202108062347-0'
{{< / highlightFile >}}
```
La version du noyau Linux est, à peu de choses près, la même sur CoreOS que sur RHEL.
{{< highlightWithTitle "RHEL 8.4" "sh" "hl_lines=3" >}}
```sh {title="RHEL 8.4" hl_lines="3"}
[nmasse@localhost ~]$ rpm -qa |grep kernel
kernel-modules-4.18.0-305.el8.x86_64
kernel-4.18.0-305.el8.x86_64
kernel-core-4.18.0-305.el8.x86_64
kernel-tools-4.18.0-305.el8.x86_64
kernel-tools-libs-4.18.0-305.el8.x86_64
{{< / highlightWithTitle >}}
```
&nbsp;
{{< highlightWithTitle "CoreOS" "sh" "hl_lines=5" >}}
```sh {title="CoreOS" hl_lines="5"}
[core@coreos ~]$ rpm -qa |grep kernel
kernel-core-4.18.0-305.10.2.el8_4.x86_64
kernel-modules-4.18.0-305.10.2.el8_4.x86_64
kernel-modules-extra-4.18.0-305.10.2.el8_4.x86_64
kernel-4.18.0-305.10.2.el8_4.x86_64
{{< / highlightWithTitle >}}
```
La glibc est exactement la même sur CoreOS et sur RHEL.
{{< highlightWithTitle "RHEL 8.4" "sh" "hl_lines=4 7" >}}
```sh {title="RHEL 8.4" hl_lines="4 7"}
[nmasse@localhost ~]$ rpm -qa |grep glibc
glibc-langpack-en-2.28-151.el8.x86_64
glibc-common-2.28-151.el8.x86_64
@ -244,11 +244,11 @@ glibc-2.28-151.el8.x86_64
[nmasse@localhost ~]$ sha1sum /lib64/libc-2.28.so
5dd511ebea3476a03d710eff1bab8a72a47fdf71 /lib64/libc-2.28.so
{{< / highlightWithTitle >}}
```
&nbsp;
{{< highlightWithTitle "CoreOS" "sh" "hl_lines=4 7" >}}
```sh {title="CoreOS" hl_lines="4 7"}
[core@coreos ~]$ rpm -qa |grep glibc
glibc-common-2.28-151.el8.x86_64
glibc-all-langpacks-2.28-151.el8.x86_64
@ -256,7 +256,7 @@ glibc-2.28-151.el8.x86_64
[core@coreos ~]$ sha1sum /lib64/libc-2.28.so
5dd511ebea3476a03d710eff1bab8a72a47fdf71 /lib64/libc-2.28.so
{{< / highlightWithTitle >}}
```
Et la [documentation OpenShift](https://docs.openshift.com/container-platform/4.9/architecture/architecture-rhcos.html) confirme ces investigations avec l'information suivante :

48
content/french/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8/index.md

@ -51,12 +51,12 @@ Je l'ai déniché pour 660 € sur eBay avec les caractéristiques suivantes :
- 48 Go de DDR4 ECC
- Carte réseau supplémentaire Intel I350 au format "FlexibleLOM", avec 4 ports RJ-45
{{< attachedFigure src="hp-dl20-gen9.png" title="Serveur HP DL20 Gen9, vue de face, vue de dos." >}}
![Serveur HP DL20 Gen9, vue de face, vue de dos.](hp-dl20-gen9.png "Serveur HP DL20 Gen9, vue de face, vue de dos.")
Pour me raccorder au réseau fibre optique d'Orange, j'ai conservé l'ONT (*Optical Network Termination*) qui m'a été fourni avec la Livebox.
D'un coté, je branche la jarretière optique, et de l'autre je branche le câble RJ-45 qui va jusqu'au serveur HP.
{{< attachedFigure src="Boitier-Fibre-Orange.jpeg" title="Boitier Fibre Orange. Source: [Wikipedia](https://commons.wikimedia.org/wiki/File:Boitier-Fibre-Orange_-_IMG_6456.jpg)" >}}
![Boitier Fibre Orange. Source: Wikipedia](Boitier-Fibre-Orange.jpeg "Boitier Fibre Orange. Source: [Wikipedia](https://commons.wikimedia.org/wiki/File:Boitier-Fibre-Orange_-_IMG_6456.jpg)")
## Logiciel
@ -106,10 +106,10 @@ sudo dnf install dhcp-client-orange-isp
Configurer NetworkManager pour qu'il utilise dhclient plutôt que son client DHCP interne.
{{< highlightFile "/etc/NetworkManager/conf.d/dhclient.conf" "ini" "hl_lines=2" >}}
```ini {filename="/etc/NetworkManager/conf.d/dhclient.conf" hl_lines="2"}
[main]
dhcp=dhclient
{{< / highlightFile >}}
```
Redémarrer NetworkManager.
@ -127,7 +127,7 @@ Pour configurer le client DHCP pour l'authentification Orange, il vous faudra tr
Saisir le contenu du fichier /etc/dhcp/dhclient.conf.
{{< highlightFile "/etc/dhcp/dhclient.conf" "c" "hl_lines=4 7-8" >}}
```c {filename="/etc/dhcp/dhclient.conf" hl_lines="4 7-8"}
option rfc3118-authentication code 90 = string;
option dhcp-client-identifier code 61 = string;
@ -138,7 +138,7 @@ interface "eno2.832" {
send rfc3118-authentication 00:00:00:00:00:00:00:00:00:00:00:1a:09:00:00:05:58:01:03:41:01:0B:66:74:69:2F:64:75:6D:6D:79:3c:12:31:32:33:34:35:36:37:38:39:30:31:32:33:34:35:36:03:13:41:b9:80:f2:ea:3f:06:3b:2b:e7:08:ac:ec:9c:38:9e:ba;
request subnet-mask,routers,domain-name,broadcast-address,dhcp-lease-time,dhcp-renewal-time,dhcp-rebinding-time,rfc3118-authentication;
}
{{< / highlightFile >}}
```
## Configuration de l'interface réseau pour utiliser le VLAN 832
@ -163,12 +163,12 @@ Aussi, considérez la comme une indication plus qu'un tutoriel pas à pas.
Activer le routage des paquets IPv4 et IPv6.
Ne pas oublier d'adapter le nom de l'interface réseau !
{{< highlightFile "/etc/sysctl.d/99-fibre-orange.conf" "ini" "hl_lines=3" >}}
```ini {filename="/etc/sysctl.d/99-fibre-orange.conf" hl_lines="3"}
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net/ipv6/conf/eno2.832/accept_ra=2
net.ipv4.conf.all.src_valid_mark=1
{{< / highlightFile >}}
```
Recharger les paramètres noyaux avec la commande **sysctl**.
@ -185,7 +185,7 @@ Même mode opératoire que pour IPv4, il vous faudra trois choses :
Saisir le contenu du fichier /etc/dhcp/dhclient6.conf.
{{< highlightFile "/etc/dhcp/dhclient.conf" "c" "hl_lines=5 9-10" >}}
```c {filename="/etc/dhcp/dhclient.conf" hl_lines="5 9-10"}
option dhcp6.auth code 11 = string;
option dhcp6.vendorclass code 16 = string;
option dhcp6.userclass code 15 = string;
@ -198,13 +198,13 @@ interface "eno2.832" {
send dhcp6.auth 00:00:00:00:00:00:00:00:00:00:00:1a:09:00:00:05:58:01:03:41:01:0B:66:74:69:2F:64:75:6D:6D:79:3c:12:31:32:33:34:35:36:37:38:39:30:31:32:33:34:35:36:03:13:41:b9:80:f2:ea:3f:06:3b:2b:e7:08:ac:ec:9c:38:9e:ba;
also request dhcp6.name-servers, dhcp6.vendorclass, dhcp6.userclass, dhcp6.auth;
}
{{< / highlightFile >}}
```
Créer le script de *dispatch* pour NetworkManager.
Ce script sera appelé automatiquement par NetworkManager après un *up* ou un *down* de l'interface réseau et lancera le client DHCPv6 en mode *Prefix Delegation*.
Ne pas oublier d'adapter le nom de l'interface réseau !
{{< highlightFile "/etc/NetworkManager/dispatcher.d/99-orange-ipv6" "sh" "hl_lines=5" >}}
```sh {filename="/etc/NetworkManager/dispatcher.d/99-orange-ipv6" hl_lines="5"}
#!/bin/bash
set -Eeuo pipefail
@ -238,7 +238,7 @@ down)
esac
exit 0
{{< / highlightFile >}}
```
Créer le répertoire **/etc/dhcp/dhclient-exit-hooks.d**.
@ -250,7 +250,7 @@ Créer le script de *hook* pour dhclient.
Il sera appelé par le client DHCPv6 après obtention d'un préfixe IPv6 et a pour tâche d'affecter les adresses IPv6 aux différentes interfaces du serveur.
Ne pas oublier d'adapter le nom de l'interface réseau et le nom de vos interfaces réseaux internes (chez moi, elles s'appelle ivs1, ivs2, etc.) !
{{< highlightFile "/etc/dhcp/dhclient-exit-hooks.d/99-orange-ipv6" "sh" "hl_lines=5 12" >}}
```sh {filename="/etc/dhcp/dhclient-exit-hooks.d/99-orange-ipv6" hl_lines="5 12"}
#!/bin/bash
set -Eeuo pipefail
@ -328,7 +328,7 @@ RELEASE)
esac
exit 0
{{< / highlightFile >}}
```
N'oubliez pas de rendre ces deux scripts exécutables.
@ -351,7 +351,7 @@ sudo systemctl mask firewalld
Éditer le contenu du fichier **/etc/sysconfig/nftables.conf**.
{{< highlightFile "/etc/sysconfig/nftables.conf" "ini" "hl_lines=" >}}
```ini {filename="/etc/sysconfig/nftables.conf"}
# Uncomment the include statement here to load the default config sample
# in /etc/nftables for nftables service.
@ -360,11 +360,11 @@ include "/etc/nftables/itix.nft"
# To customize, either edit the samples in /etc/nftables, append further
# commands to the end of this file or overwrite it after first service
# start by calling: 'nft list ruleset >/etc/sysconfig/nftables.conf'.
{{< / highlightFile >}}
```
Créer le fichier **/etc/nftables/update.nft**.
{{< highlightFile "/etc/nftables/update.nft" "ini" "hl_lines=" >}}
```ini {filename="/etc/nftables/update.nft"}
#!/usr/sbin/nft -f
flush table inet itix-fw
@ -373,11 +373,11 @@ flush table ip itix-nat
delete table ip itix-nat
include "/etc/nftables/itix.nft"
{{< / highlightFile >}}
```
Créer le fichier **/etc/nftables/itix.nft**.
{{< highlightFile "/etc/nftables/itix.nft" "c" "hl_lines=42 45 57 72 74 76 86" >}}
```c {filename="/etc/nftables/itix.nft" hl_lines="42 45 57 72 74 76 86"}
#!/usr/sbin/nft -f
table inet itix-fw {
@ -467,7 +467,7 @@ table ip itix-nat {
}
}
{{< / highlightFile >}}
```
Activer et démarrer le service **nftables**.
@ -488,7 +488,7 @@ Rien n'est documenté officiellement, mais sur le forum [lafibre.info](https://l
Pour mettre en oeuvre ces règles, j'ai développé un script qui vérifie que les piles IPv4 et IPv6 sont opérationnelles et force un nouveau cycle DHCPv4 + DHCPv6 si nécessaire.
Pour éviter tout problème, je force également un renouvellement des baux DHCP toutes les 12 heures.
{{< highlightFile "/usr/local/bin/fibre-orange" "sh" "hl_lines=5" >}}
```sh {filename="/usr/local/bin/fibre-orange" hl_lines="5"}
#!/bin/bash
set -Eeuo pipefail
@ -613,7 +613,7 @@ help)
esac
exit 0
{{< / highlightFile >}}
```
Rendre le script exécutable.
@ -623,10 +623,10 @@ sudo chmod 755 /usr/local/bin/fibre-orange
Créer la crontab associée pour lancer ce script périodiquement.
{{< highlightFile "/etc/cron.d/fibre-orange" "crontab" "hl_lines=" >}}
```crontab {filename="/etc/cron.d/fibre-orange"}
*/5 * * * * root /usr/local/bin/fibre-orange health-check
0 */12 * * * root /usr/local/bin/fibre-orange renew
{{< / highlightFile >}}
```
## Conclusion

28
content/french/blog/homelab-server-2u-short-depth-front-io-ampere-altra-arm64-architecture/index.md

@ -33,9 +33,9 @@ Ce serveur **HP DL20 Gen9** est équipé de :
- 2 disques 3,5" de 4 To chacun, configurés en RAID 1 par la carte RAID HP
- 6 ports RJ-45 à 1 GbE
{{< attachedFigure src="hp-dl20-gen9.webp" title="Le serveur HP DL20 Gen9 existant." >}}
![Le serveur HP DL20 Gen9 existant.](hp-dl20-gen9.webp "Le serveur HP DL20 Gen9 existant.")
Ce serveur est actuellement racké dans une baie informatique de 7U de haut, faible profondeur, au sous-sol de la maison et il fait actuellement tourner **CentOS Stream 8** que j'ai configuré en routeur (voir {{< internalLink path="/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8" >}}) et hyperviseur (avec **libvirt**).
Ce serveur est actuellement racké dans une baie informatique de 7U de haut, faible profondeur, au sous-sol de la maison et il fait actuellement tourner **CentOS Stream 8** que j'ai configuré en routeur (voir [](/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8)) et hyperviseur (avec **libvirt**).
L'hyperviseur fait tourner plusieurs VM :
@ -68,7 +68,7 @@ Les capacités d'expansion du **HP DL20 Gen9** sont bien limitées : 2 emplaceme
L'emplacement pleine hauteur est pris par le contrôleur RAID et l'emplacement demi hauteur est pris par la carte réseau 4 ports GbE.
Dernier point, et non des moindres, la carte mère et le CPU du **HP DL20 Gen9** ne supportent pas le **SR-IOV**. Ça m'oblige actuellement à faire tourner les fonctions de routage réseau directement sur l'hyperviseur.
La fonction SR-IOV me permettrait de mettre le {{<internalLink path="/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8" title="routeur CentOS Stream" >}} dans une VM sans perte de performance.
La fonction SR-IOV me permettrait de mettre le [routeur CentOS Stream](/blog/fibre-orange-remplacer-livebox-routeur-centos-stream-8) dans une VM sans perte de performance.
En résumé, j'ai besoin de :
@ -143,7 +143,7 @@ Il sera alors toujours temps de passer sur un CPU plus puissant.
Le CPU **Ampere Altra Q64-22** a un TDP de 69 W d'après la [fiche technique Ampere Altra](https://d1o0i0v5q5lp8h.cloudfront.net/ampere/live/assets/documents/Altra_Rev_A1_DS_v1.27_20220331.pdf).
{{< attachedFigure src="ampere-altra-q64-22.jpeg" title="Le CPU Ampere Altra Q64-22, monté sur la carte mère ASRock Rack ALTRAD8UD-1L2T" >}}
![Le CPU Ampere Altra Q64-22, monté sur la carte mère ASRock Rack ALTRAD8UD-1L2T](ampere-altra-q64-22.jpeg "Le CPU Ampere Altra Q64-22, monté sur la carte mère ASRock Rack ALTRAD8UD-1L2T")
Autre difficulté, c'est que j'habite en France et Newegg ne livre pas en France...
J'ai donc dû passer par un *reshipper* : une entreprise aux États-Unis qui réceptionne les colis et les réexpédie partout dans le monde.
@ -186,7 +186,7 @@ Avant de me décider à acheter le boîtier [Innovision M24306](http://iovserver
Le boîtier **Innovision M24306** est trouvable sur [Aliexpress](https://www.aliexpress.us/item/1005005856237609.html) et [Alibaba](https://www.alibaba.com/product-detail/Ultra-Short-2U-rackmount-Server-Chassis_1600636420062.html).
{{< attachedFigure src="innovision-m24306.jpeg" title="Le boîtier Innovision M24306." >}}
![Le boîtier Innovision M24306.](innovision-m24306.jpeg "Le boîtier Innovision M24306.")
Quelques remarques utiles (j'ai demandé des clarifications avant de passer commande) :
@ -199,7 +199,7 @@ Quelques remarques utiles (j'ai demandé des clarifications avant de passer comm
Note : la prise VGA que l'on voit sur l'oreille droite du serveur n'est connectée à rien du tout.
Mais ce n'est pas gênant dans le sens où le boîtier a ses entrées/sorties sur la face avant donc la prise VGA est déjà sur la face avant et n'a pas besoin d'être déportée.
{{< attachedFigure src="vga-not-connected.jpeg" title="La prise VGA sur l'oreille droite du boîtier Innovision M24306 n'est pas connectée." >}}
![La prise VGA sur l'oreille droite du boîtier Innovision M24306 n'est pas connectée.](vga-not-connected.jpeg "La prise VGA sur l'oreille droite du boîtier Innovision M24306 n'est pas connectée.")
## Alimentation CRPS FSP
@ -227,7 +227,7 @@ Le modèle [FSP-FC250](https://www.fsp-group.com/download/pro/FSP-FC250_Datashee
J'ai ensuite opté pour le module d'alimentation de la plus petite puissance disponible car 2 x 550W, ça fait déjà beaucoup pour un petit serveur !
C'est le modèle [FSP550-20FM](https://www.fsp-group.com/download/pro/FSP550-20FM_Datasheet.pdf).
{{< attachedFigure src="psu-fsp.webp" title="La composition de l'alimentation CRPS de marque FSP." >}}
![La composition de l'alimentation CRPS de marque FSP.](psu-fsp.webp "La composition de l'alimentation CRPS de marque FSP.")
Il est à noter que la *Power Distribution Board* n'est fixée au boîtier que par deux petites vis.
Pour éviter qu'elle ne bouge, j'ai dû la caler avec un morceau de mousse.
@ -256,7 +256,7 @@ La référence **CAB-8654/8654-8i-11-P0.5M-85** chez [10Gtek](https://www.10gtek
- Enfin, il est possible d'opter pour des connecteurs coudés ou droits.
J'ai pris les **connecteurs droits**.
{{< attachedFigure src="slimsas-cables.webp" title="Cheminement des câbles SlimSAS / SFF-8654 dans le boîtier Innovision M24306." >}}
![Cheminement des câbles SlimSAS / SFF-8654 dans le boîtier Innovision M24306.](slimsas-cables.webp "Cheminement des câbles SlimSAS / SFF-8654 dans le boîtier Innovision M24306.")
## Stockage
@ -270,7 +270,7 @@ Ces SSD sont au format *Add-in Card (AIC)*, demi-hauteur, demi-longueur (**HHHL*
Mais dans l'immédiat, ça me permet d'avancer.
Et si je trouve une enchère eBay avec un lot de SSD Samsung PM1733 à bon prix, je n'aurais qu'à transférer les données sur les nouveaux SSD pour récupérer 3 emplacements PCIe.
{{< attachedFigure src="nvme-storage.jpeg" title="Trois SSD Samsung PM1735 et un SSD Samsung 980 PRO installés dans le boîtier." >}}
![Trois SSD Samsung PM1735 et un SSD Samsung 980 PRO installés dans le boîtier.](nvme-storage.jpeg "Trois SSD Samsung PM1735 et un SSD Samsung 980 PRO installés dans le boîtier.")
Il est à noter que les SSD **Samsung PM1733A** ont été testés par la société **Ampere Computing** et ont montré de bonnes performances avec leur CPU ! Voir [Samsung FIO Performance - Solution Brief](https://amperecomputing.com/briefs/samsung-FIO-performance).
@ -278,7 +278,7 @@ J'ai ajouté à cela un SSD NVMe au format M.2 pour héberger le système d'expl
Par soucis du détail, j'ai quand même testé 6 SSD NVMe au format U.2 (PCIe 3.0) sur le *backplane NVMe* du boîtier. Ça fonctionne ! En revanche, je n'ai pas testé le branchage/débranchage à chaud (*hotplug*) car je ne pense pas que le *backplane* de ce boîtier ait implémenté la fonctionnalité.
{{< attachedFigure src="more-nvme-storage.webp" title="Six SSD NVMe de plus, pour un total de 26.4 To de stockage NVMe !" >}}
![Six SSD NVMe de plus, pour un total de 26.4 To de stockage NVMe !](more-nvme-storage.webp "Six SSD NVMe de plus, pour un total de 26.4 To de stockage NVMe !")
## Nomenclature
@ -306,7 +306,7 @@ Et dans ce prix, je n'ai pas compté les frais de port, les droits de douane, et
Il est à noter que, par défaut, le flux d'air des ventilateurs des alimentations **FSP 550-20FM** et du boîtier **Innovision M24306** sont inversés : les ventilateurs des alimentations soufflent dans le sens **avant -> arrière** alors que les ventilateurs du boîtier soufflent dans le sens **arrière -> avant**.
Fort heureusement, les ventilateurs du boîtier sont réversibles.
{{< attachedFigure src="fans-air-flow.webp" title="J'ai retourné les quatre ventilateurs du avoir un flux d'air avant -> arrière. Dans cette configuration, les étiquettes des ventilateurs ne sont pas visibles de l'intérieur du boîtier." >}}
![J'ai retourné les quatre ventilateurs du avoir un flux d'air avant -> arrière. Dans cette configuration, les étiquettes des ventilateurs ne sont pas visibles de l'intérieur du boîtier.](fans-air-flow.webp "J'ai retourné les quatre ventilateurs du avoir un flux d'air avant -> arrière. Dans cette configuration, les étiquettes des ventilateurs ne sont pas visibles de l'intérieur du boîtier.")
Je n'ai pas encore fait de test de performance, mais les températures mesurées sont plus basses avec ce flux d'air optimisé.
C'est au niveau des 6 SSD NVMe à gauche que l'effet est le plus notable.
@ -319,15 +319,15 @@ Les trois emplacements inutilisés peuvent être masqués pour forcer le flux d'
L'aspect extérieur du produit est plutôt flatteur, presque professionnel.
{{< attachedFigure src="final-product.webp" title="Serveur 2U basé sur un boîtier Innovision M24306, une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22. Face avant en haut, face arrière en bas." >}}
![Serveur 2U basé sur un boîtier Innovision M24306, une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22. Face avant en haut, face arrière en bas.](final-product.webp "Serveur 2U basé sur un boîtier Innovision M24306, une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22. Face avant en haut, face arrière en bas.")
L'intérieur est moins bien agencé qu'un serveur HP ou Dell mais je saurais m'en satisfaire. 😎
{{< attachedFigure src="internal-layout.webp" title="Agencement interne du boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22." >}}
![Agencement interne du boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22.](internal-layout.webp "Agencement interne du boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22.")
Dernières photos, cette fois-ci avec le couvercle en place, prêt à être racké ! 🚀
{{< attachedFigure src="perspective-view.webp" title="Boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22." >}}
![Boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22.](perspective-view.webp "Boîtier Innovision M24306 avec une carte mère ASRock Rack ALTRAD8UD-1L2T et un CPU Ampere Altra Q64-22.")
## Conclusion

14
content/french/blog/installation-mise-jour-openshift-environnement-deconnecte/index.md

@ -150,7 +150,7 @@ En effet, ce fichier openshift-install n'est valide que pour une install de **ce
Créer le fichier install-config.yaml comme pour une installation connectée, en fonction de vos choix d'architecture et de votre environnement.
Ajouter la section **imageContentSources** comme indiqué ci-dessus.
{{< highlight yaml "hl_lines=26-34" >}}
```yaml {hl_lines="26-34"}
apiVersion: v1
baseDomain: itix.xyz
compute:
@ -185,7 +185,7 @@ imageContentSources:
- registry.itix.xyz/openshift-mirror/ocp4
- registry.itix.xyz/openshift-mirror/ocp4-release
source: quay.io/openshift-release-dev/ocp-v4.0-art-dev
{{< / highlight >}}
```
Puis lancer l'installation comme pour une installation connectée.
@ -316,11 +316,11 @@ EOF
Créer un fichier **Dockerfile** avec le contenu suivant.
{{< highlightFile "Dockerfile" "docker" "" >}}
```docker {filename="Dockerfile"}
FROM registry.access.redhat.com/ubi8/ubi:8.1
RUN curl -L -o cincinnati-graph-data.tar.gz https://github.com/openshift/cincinnati-graph-data/archive/master.tar.gz
CMD exec /bin/bash -c "tar xvzf cincinnati-graph-data.tar.gz -C /var/lib/cincinnati/graph-data/ --strip-components=1"
{{< / highlightFile >}}
```
Construire l'image correspondante et la pousser dans la registry privée.
@ -369,15 +369,15 @@ oc patch clusterversion version -p "{\"spec\":{\"upstream\":\"${POLICY_ENGINE_GR
Pour préparer la mise à jour, j'utilise le [Red Hat OpenShift Container Platform Update Graph](https://access.redhat.com/labs/ocpupgradegraph/update_path).
{{< attachedFigure src="update-graph-1.png" title="Saisir la version source, la version cible et le canal de mise à jour." >}}
![Saisir la version source, la version cible et le canal de mise à jour.](update-graph-1.png "Saisir la version source, la version cible et le canal de mise à jour.")
Il m'a permis de découvrir que le channel **fast-4.8** me permet de passer de la 4.7.10 à la 4.8.5 avec une seule étape intermédiaire: la 4.7.24.
{{< attachedFigure src="update-graph-2.png" title="En utilisant le canal fast-4.8, je peux passer de la 4.7.10 à la 4.7.24 à la 4.8.5." >}}
![En utilisant le canal fast-4.8, je peux passer de la 4.7.10 à la 4.7.24 à la 4.8.5.](update-graph-2.png "En utilisant le canal fast-4.8, je peux passer de la 4.7.10 à la 4.7.24 à la 4.8.5.")
L'outil génère aussi un graphe contenant toutes les versions disponibles et les chemins autorisés entre ces versions.
{{< attachedFigure src="update-graph-3.png" title="Le graphe de mise à jour résultant." >}}
![Le graphe de mise à jour résultant.](update-graph-3.png "Le graphe de mise à jour résultant.")
Nous effectuerons donc une mise à jour en deux étapes: d'abord une mise à jour vers la 4.7.24, puis vers la 4.8.5.

2
content/french/speaking/demobox-redhat-sso/index.md

@ -14,7 +14,7 @@ Au travers d'une série de trois vidéos, je présente Red Hat Single Sign On (l
Je commence par l'installation, la fédération des identités, puis vient la sécurisation d'une application NodeJS.
Et enfin, on termine par la sécurisation d'une application Quarkus.
{{< screenshotOf src="tweet-RedHatFrance.png" href="https://twitter.com/RedHatFrance/status/1390660299011895296" >}}
[![Tweet de @RedHatFrance](tweet-RedHatFrance.png)](https://twitter.com/RedHatFrance/status/1390660299011895296)
Retrouvez ci-dessous tous les liens vers les vidéos !

21
content/french/speaking/devoxx-france-2019/index.md

@ -20,15 +20,18 @@ Le replay est sur la [chaîne YouTube de DevoxxFR](https://www.youtube.com/watch
## Photos
{{< attachedFigure src="on-stage-1.jpg" >}}
{{< attachedFigure src="on-stage-2.jpg" >}}
{{< attachedFigure src="on-stage-4.jpg" >}}
{{< attachedFigure src="on-stage-5.jpg" >}}
![](on-stage-1.jpg)
![](on-stage-2.jpg)
![](on-stage-4.jpg)
![](on-stage-5.jpg)
## Réactions du public
{{< screenshotOf src="tweet-lbroudoux.png" href="https://twitter.com/lbroudoux/status/1119221025466720256" >}}
{{< screenshotOf src="tweet-gbloquel.png" href="https://twitter.com/gbloquel/status/1119170710696493056" >}}
{{< screenshotOf src="tweet-sebi2706.png" href="https://twitter.com/sebi2706/status/1119169813392314368" >}}
{{< screenshotOf src="tweet-cgodard.png" href="https://twitter.com/cgodard/status/1119159971835289600" >}}
{{< screenshotOf src="tweet-PetitMel_issa.png" href="https://twitter.com/PetitMel_issa/status/1119170065289678848" >}}
[![Tweet de @lbroudoux](tweet-lbroudoux.png)](https://twitter.com/lbroudoux/status/1119221025466720256)
[![Tweet de @gbloquel](tweet-gbloquel.png)](https://twitter.com/gbloquel/status/1119170710696493056)
[![Tweet de @sebi2706](tweet-sebi2706.png)](https://twitter.com/sebi2706/status/1119169813392314368)
[![Tweet de @cgodard](tweet-cgodard.png)](https://twitter.com/cgodard/status/1119159971835289600)
[![Tweet de @PetitMel_issa](tweet-PetitMel_issa.png)](https://twitter.com/PetitMel_issa/status/1119170065289678848)

8
content/french/speaking/devoxx-france-2025/index.md

@ -25,15 +25,15 @@ Un moment fort, mêlant technologie, fun et culture geek, que j’ai voulu parta
## Mission IApossible: Arrêtez le train Lego à temps grâce à l’IA et l’Edge
{{< attachedFigure src="on-stage1.jpeg" title="Mise en place de la démo terminée, on peut commencer !" >}}
![Mise en place de la démo terminée, on peut commencer !](on-stage1.jpeg "Mise en place de la démo terminée, on peut commencer !")
Ma session portait le titre évocateur "[Mission IApossible: Arrêtez le train Lego à temps grâce à l’IA et l’Edge](https://www.devoxx.fr/agenda-2025/talk/mission-iapossible-arretez-le-train-lego-a-temps-grace-a-l-ia-et-l-edge/)". L’objectif ? Montrer de manière ludique et technique comment des projets mélant **Intelligence Artificielle** et **Edge Computing** être réalisés avec les technologies [Red Hat](https://www.redhat.com/fr).
{{< attachedFigure src="quote.png" >}}
![](quote.png)
Imaginez sur scène : un train **Lego City #60337**, boosté avec un **Jetson Orin** de Nvidia, embarque une IA capable de reconnaître les panneaux de signalisation et… de s’arrêter juste à temps. Dans le rôle de chef d’orchestre ? Des technologies comme **YOLO** pour la détection d’objets, **Jupyter** pour l’expérimentation, **Quarkus** pour l’application embarquée, et **Tekton Pipelines** pour la chaîne d'intégration continue et **microshift** pour le déploiement. Bref, un condensé d’innovation dans un décor Lego, avec pour toile de fond l’univers de Mission Impossible.
{{< attachedFigure src="train.jpeg" title="La mise en place du train sur scène en seulement 20 minutes, c'était ça la mission impossible !" >}}
![La mise en place du train sur scène en seulement 20 minutes, c'était ça la mission impossible !](train.jpeg "La mise en place du train sur scène en seulement 20 minutes, c'était ça la mission impossible !")
Au-delà de l’aspect ludique, l’enjeu était aussi de montrer la capacité d'embarquer un modèle d'intelligence artificielle à l'Edge, dans un cadre concret et tangible. Car oui, ce genre de cas d’usage s’applique bien au-delà du monde Lego : smart cities, véhicules autonomes, automatisation industrielle… les possibilités sont multiples.
@ -47,4 +47,4 @@ Un succès que j’ai eu un immense plaisir à partager avec une salle bien remp
## Ressources
- [Le code source des pipelines Tekton multi-architecture](https://github.com/nmasse-itix/tekton-pipeline-multiarch/tree/devoxx-france-2025)
- [Les supports de présentation]({{< attachedFileLink src="slides.pdf" >}})
- [Les supports de présentation](slides.pdf)

8
content/french/speaking/mad-roadshow-paris-2023/index.md

@ -14,7 +14,7 @@ opensource:
Le 6 Juin 2023, j'ai participé à l'événement [MAD Roadshow Paris 2023](https://events.redhat.com/profile/form/index.cfm?PKformID=0x784359abcd&sc_cid=7013a0000034l4oAAA) durant lequel j'ai montré une démo intitulée "Ease app development and deployment - From a laptop to production, the great and easy way".
{{< attachedFigure src="slide.png" >}}
![](slide.png)
La démonstration combine de nombreux outils de la plateforme [Red Hat OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift) :
@ -31,5 +31,7 @@ Le code de la démo est disponible sur GitHub :
Quelques photos de l'évènement :
{{< attachedFigure src="20230606_114542.jpeg" >}}
{{< attachedFigure src="20230606_114804.jpeg" >}}
![](20230606_114542.jpeg)
![](20230606_114804.jpeg)

10
content/french/speaking/matinale-it-for-business-devsecops-2022/index.md

@ -33,8 +33,8 @@ Quelques constats chiffrés :
Mon intervention a été reprise par les journalistes dans certains tweets :
{{< screenshotOf src="tweet-ITforB-01.png" href="https://twitter.com/ITforB/status/1544241974848487425" >}}
{{< screenshotOf src="tweet-ITforB-02.png" href="https://twitter.com/thierryderouet/status/1544240686303744002" >}}
{{< screenshotOf src="tweet-ITforB-03.png" href="https://twitter.com/thierryderouet/status/1544240966097375233" >}}
{{< screenshotOf src="tweet-ITforB-04.png" href="https://twitter.com/thierryderouet/status/1544241464326201345" >}}
{{< screenshotOf src="tweet-ITforB-05.png" href="https://twitter.com/thierryderouet/status/1544241708271116289" >}}
[![Tweet de @ITforB](tweet-ITforB-01.png)](https://twitter.com/ITforB/status/1544241974848487425)
[![Tweet de @ITforB](tweet-ITforB-02.png)](https://twitter.com/thierryderouet/status/1544240686303744002)
[![Tweet de @ITforB](tweet-ITforB-03.png)](https://twitter.com/thierryderouet/status/1544240966097375233)
[![Tweet de @ITforB](tweet-ITforB-04.png)](https://twitter.com/thierryderouet/status/1544241464326201345)
[![Tweet de @ITforB](tweet-ITforB-05.png)](https://twitter.com/thierryderouet/status/1544241708271116289)

6
content/french/speaking/openshift-ai-roadshow-2024/index.md

@ -20,11 +20,11 @@ La démo est basée sur le scénario du 7ème opus de la saga **Mission Impossib
**Ethan Hunt** a besoin d'aide pour arrêter le train.
Grâce un modèle d'intelligence artificielle conçu dans **OpenShift AI** et déployé sur un **Nvidia Jetson Orin** faisant tourner **Red Hat Device Edge**, le train reconnait les panneaux de signalisation et s'arrête tout seul !
{{< attachedFigure src="le-train-lego.jpeg" title="Le train Lego équipé de la webcam, du Nvidia Jetson Orin et de la batterie." >}}
![Le train Lego équipé de la webcam, du Nvidia Jetson Orin et de la batterie.](le-train-lego.jpeg "Le train Lego équipé de la webcam, du Nvidia Jetson Orin et de la batterie.")
Et c'est à l'occasion de cette démo que j'ai écrit un article sur intitulé {{< internalLink path="/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md" >}}.
Et c'est à l'occasion de cette démo que j'ai écrit un article sur intitulé [](/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md).
{{< attachedFigure src="train-console-1.png" title="Vue de la caméra embarquée du train avec les résultats de l'inférence." >}}
![Vue de la caméra embarquée du train avec les résultats de l'inférence.](train-console-1.png "Vue de la caméra embarquée du train avec les résultats de l'inférence.")
La démo est en train d'être affinée pour être présentée dans les prochains événements Red Hat.
Venez nous voir !

12
content/french/speaking/platform-day-2024/index.md

@ -20,7 +20,7 @@ Le 28 Mai 2024, j'ai participé à l'événement [Platform Day](https://events.r
La démo mets en scène l'équipe du film **Mission Impossible** qui a fait une halte inattendue au Pavillon Royal pour offrir aux invités un aperçu de l'une des scènes les plus audacieuses du nouvel épisode.
Le film met en vedette Ethan Hunt et son équipe, qui doivent déjouer un complot visant à prendre le contrôle des Kubernetes de la planète. 😄
{{< attachedFigure src="equipe.jpeg" title="L'équipe en charge de la démo Mission Impossible." >}}
![L'équipe en charge de la démo Mission Impossible.](equipe.jpeg "L'équipe en charge de la démo Mission Impossible.")
Les personnages principaux de cette mission sont :
@ -41,7 +41,7 @@ Dans cette mission, mon rôle a consisté à **prendre le contrôle du train**.
Le train est équipé d’un moteur et d’un Hub Lego.
Le Hub Lego reçoit les ordres d’accélération, décélération et freinage via le protocole **Bluetooth Low Energy**.
{{< attachedFigure src="architecture-materielle.png" title="L'architecture matérielle mise en oeuvre dans la démonstration." >}}
![L'architecture matérielle mise en oeuvre dans la démonstration.](architecture-materielle.png "L'architecture matérielle mise en oeuvre dans la démonstration.")
Nous avons intégré une carte **Nvidia Jetson Orin** dans le train Lego.
La carte Nvidia Jetson Orin est un *System on Chip* (SoC) qui intègre tous les composants nécessaires à notre mission : CPU, RAM, stockage et un puissant GPU pour accélérer les calculs.
@ -60,7 +60,7 @@ Le flux vidéo est relayé depuis le Jetson via un **broker Kafka**.
De plus, des pipelines MLOps sont mis en place pour entraîner le modèle d'intelligence artificielle, ainsi que des pipelines CI/CD pour construire les images de conteneurs de nos microservices pour les architectures x86 et ARM.
{{< attachedFigure src="mission-edge.png" title="Les points clés de ma mission." >}}
![Les points clés de ma mission.](mission-edge.png "Les points clés de ma mission.")
Durant cette mission, j'ai fait face à trois principaux défis.
@ -88,7 +88,7 @@ Il suffit d'ajouter la dépendance vers la bibliothèque **node-poweredup** patc
}
```
{{< attachedFigure src="challenges-edge.png" title="Les défis que j'ai pu rencontrer lors de ma mission." >}}
![Les défis que j'ai pu rencontrer lors de ma mission.](challenges-edge.png "Les défis que j'ai pu rencontrer lors de ma mission.")
Le **second défi** a été de préparer des images du système d'exploitation (**Red Hat Enterprise Linux 9**), adaptées pour la carte **Nvidia Jetson Orin Nano**.
Pour cela, j'ai utilisé l'outil [composer-cli](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/composing_installing_and_managing_rhel_for_edge_images/composing-a-rhel-for-edge-image-using-image-builder-command-line_composing-installing-managing-rhel-for-edge-images#network_based_deployments_workflow).
@ -104,7 +104,7 @@ Il a donc fallu récupérer auprès de l'*engineering* RHEL les modules noyau Nv
Le **dernier défi** a été de concevoir des **pipelines CI/CD** pour créer des images de conteneur multi-architecture.
En effet, les puces M1 d'Apple ont une architecture **arm64**, les PC sont en **x86_64**.
Idem, les tests d'intégration sont souvent exécutés sur des serveurs dans le cloud (**x86_64**) alors que le déploiement se fait sur la carte Nvidia Jetson Orin Nano (**arm64**).
J'ai détaillé la procédure complète dans l'article intitulé {{< internalLink path="/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md" >}}.
J'ai détaillé la procédure complète dans l'article intitulé [](/blog/build-multi-architecture-container-images-with-kubernetes-buildah-tekton-aws/index.md).
## Genius Bar
@ -112,7 +112,7 @@ L'après-midi, nous avons animé un atelier *Genius Bar*, où beaucoup de partic
L'afflux était tel que nous n'avons pas trouvé 5 minutes pour démarrer le train.
C'était intense ! 🥵
{{< attachedFigure src="genius-bar.jpeg" title="Le Genius Bar de la démo Mission Impossible." >}}
![Le Genius Bar de la démo Mission Impossible.](genius-bar.jpeg "Le Genius Bar de la démo Mission Impossible.")
## Conclusion

6
content/french/speaking/red-hat-allie-strategie-edge/index.md

@ -11,12 +11,12 @@ Le jeudi 28 mars à 10h, j'ai animé un webinaire sur le thème du *Edge Computi
<!--more-->
{{< screenshotOf src="tweet-RedHatFrance.png" href="https://twitter.com/RedHatFrance/status/1768201675481698412" >}}
[![Tweet de @RedHatFrance](tweet-RedHatFrance.png)](https://twitter.com/RedHatFrance/status/1768201675481698412)
Lors du webinaire, une introduction à l'*Edge Computing* a été présentée, suivie d'une exploration des solutions spécifiques de Red Hat pour cette technologie.
La session s'est achevée avec un cas client.
{{< attachedFigure src="agenda.png" >}}
![](agenda.png)
Bien que l'*Edge Computing* ne soit pas un concept nouveau, il continue de gagner en popularité grâce à sa mise en oeuvre dans divers secteurs tels que les télécommunications, le commerce de détail et l'industrie.
Cette technologie se caractérise par le déploiement d'applications au plus près des données et des utilisateurs, englobant une diversité d'applications et de technologies.
@ -28,7 +28,7 @@ Malgré ces défis, les avantages tels que l'amélioration de la rapidité de pr
Red Hat a introduit **Red Hat Device Edge**, une solution caractérisée par sa modularité, sa flexibilité et sa cohérence.
Cette solution comprend **Red Hat Enterprise Linux**, optimisé pour l'*Edge Computing*, **Microshift** — une version allégée de Kubernetes adaptée à cette technologie — et **Ansible Automation Platform** pour une gestion simplifiée à grande échelle.
{{< attachedFigure src="redhat-device-edge.png" >}}
![](redhat-device-edge.png)
**Red Hat Device Edge** a été conçu pour offrir une flexibilité en termes de configuration, permettant aux utilisateurs de choisir entre différentes combinaisons de systèmes d'exploitation, d'orchestration de conteneurs et d'automatisation pour optimiser les coûts.

9
content/french/speaking/red-hat-open-tour-lyon-2025/index.md

@ -21,14 +21,15 @@ opensource:
Le 20 Mai 2025, j'ai participé à l'événement [Red Hat Open Tour](https://events.redhat.com/profile/form/index.cfm?PKformID=0x1406172abcd&extIdCarryOver=true&sc_cid=701f2000001Css5AAC) durant lequel j'ai animé, avec mes collègues Anisse et Mourad, un atelier de travaux pratiques combinant **Edge Computing** et **Intelligence Artificielle**.
[L'énoncé de l'atelier](https://open-tour-2025.netlify.app/fr/) est basé sur ce que nous avions présenté lors du {{< internalLink path="/speaking/red-hat-open-tour-toulouse-2024/index.md" >}} : grâce un modèle d'intelligence artificielle conçu dans **OpenShift AI** et déployé sur un **Nvidia Jetson Orin** faisant tourner **Red Hat Device Edge**, le train reconnait les panneaux de signalisation et s'arrête tout seul lorsque la signalisation le lui impose !
[L'énoncé de l'atelier](https://open-tour-2025.netlify.app/fr/) est basé sur ce que nous avions présenté lors du [](/speaking/red-hat-open-tour-toulouse-2024/index.md) : grâce un modèle d'intelligence artificielle conçu dans **OpenShift AI** et déployé sur un **Nvidia Jetson Orin** faisant tourner **Red Hat Device Edge**, le train reconnait les panneaux de signalisation et s'arrête tout seul lorsque la signalisation le lui impose !
## Photos
{{< attachedFigure src="participants-1.jpeg" >}}
{{< attachedFigure src="participants-2.jpeg" >}}
![](participants-1.jpeg)
![](participants-2.jpeg)
## Ressources
- [Énoncé de l'atelier](https://open-tour-2025.netlify.app/fr/)
- [Supports de présentation]({{< attachedFileLink src="slides.pdf" >}})
- [Supports de présentation](slides.pdf)

6
content/french/speaking/red-hat-open-tour-toulouse-2024/index.md

@ -21,9 +21,9 @@ opensource:
Le 28 Novembre 2024, j'ai participé à l'événement [Red Hat Open Tour](https://events.redhat.com/profile/form/index.cfm?PKformID=0x1275737abcd) durant lequel j'ai animé, avec mes collègues Adrien et Mourad, un atelier de travaux pratiques combinant **Edge Computing** et **Intelligence Artificielle**.
{{< attachedFigure src="mas-tolosa.jpeg" title="L'événement s'est déroulé au Mas Tolosa, à proximité de Toulouse." >}}
![L'événement s'est déroulé au Mas Tolosa, à proximité de Toulouse.](mas-tolosa.jpeg "L'événement s'est déroulé au Mas Tolosa, à proximité de Toulouse.")
[L'énoncé de l'atelier](https://open-tour-2024.netlify.app/fr/) est basé sur ce que nous avions présenté lors du {{< internalLink path="/speaking/riviera-dev-2024/index.md" >}} : le scénario du 7ème opus de la saga **Mission Impossible: Dead Reckoning**... avec un train Lego !
[L'énoncé de l'atelier](https://open-tour-2024.netlify.app/fr/) est basé sur ce que nous avions présenté lors du [](/speaking/riviera-dev-2024/index.md) : le scénario du 7ème opus de la saga **Mission Impossible: Dead Reckoning**... avec un train Lego !
**Ethan Hunt** a besoin d'aide pour arrêter le train.
Grâce un modèle d'intelligence artificielle conçu dans **OpenShift AI** et déployé sur un **Nvidia Jetson Orin** faisant tourner **Red Hat Device Edge**, le train reconnait les panneaux de signalisation et s'arrête tout seul !
@ -41,7 +41,7 @@ L’accent a été mis sur l’utilisation des fonctionnalités d’**OpenShift
Cette approche a permis de démystifier les étapes clés d'un projet d'Intelligence Artificielle.
{{< attachedFigure src="participants.jpeg" title="Les participants de cet atelier de travaux pratiques ont développé, durant 3 heures, le pilote automatique du train Lego." >}}
![Les participants de cet atelier de travaux pratiques ont développé, durant 3 heures, le pilote automatique du train Lego.](participants.jpeg "Les participants de cet atelier de travaux pratiques ont développé, durant 3 heures, le pilote automatique du train Lego.")
Pour rendre l’exercice accessible et réduire les prérequis matériels, la suite de l’atelier s’est appuyé sur une approche ingénieuse.
Plutôt que d’exiger un set Lego complet plus une carte Jetson Orin Nano par participant, tout a été conteneurisé et bouchonné :

3
content/french/speaking/red-hat-openshift-user-group-3/index.md

@ -20,4 +20,5 @@ Durant la session, de nombreuses questions ont été abordées :
- Quel modèles organisationnels au niveau du dépôt Git ? un dépot par environnement ? une branche par environnement ? quelles droits d'accès ?
- Comment sont gérées les approbations pour le déploiement en production ?
{{< attachedFigure src="photo-participants.jpeg" >}}
![](photo-participants.jpeg)

3
content/french/speaking/red-hat-openshift-user-group-4/index.md

@ -12,4 +12,5 @@ opensource:
Le 5 Décembre 2023, j'ai participé à l'événement [OpenShift User Group #4](https://events.redhat.com/profile/form/index.cfm?PKformID=0x951578abcd) durant lequel j'ai animé les groupes de travail *"Observabilité au sein d’OpenShift"* et *"Cluster as a Service"*.
{{< attachedFigure src="photo-participants.jpeg" >}}
![](photo-participants.jpeg)

7
content/french/speaking/red-hat-openshift-user-group-5/index.md

@ -13,7 +13,7 @@ opensource:
Le 14 Mai 2024, j'ai participé à l'événement [OpenShift User Group #5](https://events.redhat.com/profile/form/index.cfm?PKformID=0x1014468abcd) [💾](https://web.archive.org/web/20240522122155/https://events.redhat.com/profile/form/index.cfm?PKformID=0x1014468abcd) durant lequel j'ai animé les groupes de travail *"Sécurité des Conteneurs"* et *"Gestion multi-cluster"*.
{{< attachedFigure src="atelier-gestion-multi-cluster.jpeg" >}}
![](atelier-gestion-multi-cluster.jpeg)
Les deux ateliers se sont déroulés de manière collaborative :
@ -23,6 +23,7 @@ Les deux ateliers se sont déroulés de manière collaborative :
Dans les graphes ci-dessous, les thèmes couverts sont en bleu et les thèmes restant à couvrir sont en gris.
{{< attachedFigure src="atelier-gestion-multi-cluster.svg" title="Sujets abordés lors de l'atelier \"Gestion multi-cluster\"." >}}
![Sujets abordés lors de l'atelier "Gestion multi-cluster".](atelier-gestion-multi-cluster.svg "Sujets abordés lors de l'atelier \"Gestion multi-cluster\".")
![Sujets abordés lors de l'atelier "Sécurité des conteneurs".](atelier-securite-conteneur.svg "Sujets abordés lors de l'atelier \"Sécurité des conteneurs\".")
{{< attachedFigure src="atelier-securite-conteneur.svg" title="Sujets abordés lors de l'atelier \"Sécurité des conteneurs\"." >}}

10
content/french/speaking/red-hat-summit-connect-france-2023/index.md

@ -16,7 +16,7 @@ Il y avait plus de 600 personnes ce jour là.
Retrouvez ci-dessous les principaux éléments de ma *Keynote* !
{{< attachedFigure src="RedHat.HL-56.jpeg" >}}
![](RedHat.HL-56.jpeg)
Le DevOps est un sujet absolument **passionnant**, pour lequel les entreprises en attendent de **produire plus de valeur, avec toujours moins de coûts**.
Mais rien de tout cela n'est vraiment **nouveau**.
@ -56,7 +56,7 @@ Developer Hub vous offre une plateforme développeur Open Source, avec la stabil
Developer Hub **est basé sur Red Hat OpenShift** et tire parti des compétences que vous avez déjà sur OpenShift.
Et c’est probablement l’information la plus importante de la journée : **si vous avez déjà OpenShift, Red Hat Developer Hub vous tend les bras pour améliorer la productivité de vos développeurs.**
{{< attachedFigure src="RedHat.HL-61.jpeg" >}}
![](RedHat.HL-61.jpeg)
Et je ne peux pas terminer cette partie sur le développement applicatif sans parler du sujet à la mode : **l’intelligence artificielle**.
L’IA est utilisée pour résoudre des problèmes compliqués, qui s’il devaient être implémentés de manière traditionnelle, ne le serait pas ou alors trop coûteux pour être rentable.
@ -75,7 +75,7 @@ OpenShift AI vous permet de créer, entraîner et mettre à disposition vos mod
Et après vous avoir parlé de DevOps et d’IA, abordons le thème de la sécurité informatique.
{{< attachedFigure src="RedHat.HL-65.jpeg" >}}
![](RedHat.HL-65.jpeg)
La sécurité informatique, c’est un peu le parent pauvre de notre industrie: **on y pense quand il est trop tard**, quand le problème est là.
Cette situation est exacerbée par la **complexité** et l’étendue des architectures logicielles.
@ -119,8 +119,8 @@ Permettez-moi de vous présenter **Red Hat Trusted Software Supply Chain**, notr
C’est une sélections de packages, bibliothèque et dépendances spécialement sélectionnés par Red Hat.
C’est également une plateforme de CI pour construire des applicatifs de manière sécurisée.
{{< attachedFigure src="RedHat.HL-63.jpeg" >}}
![](RedHat.HL-63.jpeg)
[Téléchargez les slides de ma Keynote !]({{< attachedFileLink src="slides.pdf" >}})
[Téléchargez les slides de ma Keynote !](slides.pdf)
À l'année prochaine !

26
content/french/speaking/red-hat-summit-connect-france-2024/index.md

@ -33,7 +33,7 @@ Le tout en les plongeant dans une aventure captivante où chaque exercice impliq
Lors de cet atelier, les participants devaient développer pas moins de quatre micro-services pour construire une application de simulation de combat entre super héros et super villains.
{{< attachedFigure src="open-code-quest-microservices.png" >}}
![](open-code-quest-microservices.png)
Les micro-services ont été développés en Quarkus, le framework Java natif pour le cloud, en démontrant comment il peut transformer le développement d’applications en alliant rapidité de développement, légèreté et performance.
En particulier, Quarkus réduit considérablement l'empreinte mémoire des applications, tout en permettant un démarrage quasi instantané.
@ -49,7 +49,7 @@ Les participants ont pu découvrir comment ces outils permettent de renforcer la
Je vous laisse découvrir la liste complète de l'outillage utilisé dans l'atelier **Open Code Quest** :
{{< attachedFigure src="open-code-quest-namespaces.png" >}}
![](open-code-quest-namespaces.png)
Avant et pendant l'**Open Code Quest**, la gestion de la **plateforme** a joué un rôle clé dans la réussite de l'événement.
En tant que membre organisateur, j’ai eu la responsabilité, avec [Sébastien Lallemand](https://sebastienlallemand.net/), de préparer, dimensionner, installer et configurer les huit clusters OpenShift nécessaires au bon déroulement des ateliers.
@ -58,7 +58,7 @@ Cette phase cruciale de préparation a permis de garantir une infrastructure sta
Pendant l’événement, mon rôle de SRE (Site Reliability Engineer) consistait à surveiller de près les métriques critiques, telles que l'utilisation des ressources, afin d'assurer une expérience fluide et optimale pour tous les participants.
Grâce à cette surveillance proactive nous avons pu offir une disponibilité constante des environnements et ainsi faciliter le bon déroulement de l'atelier.
{{< attachedFigure src="open-code-quest-clusters.png" >}}
![](open-code-quest-clusters.png)
Un autre défi que j'ai relevé pour l'Open Code Quest a été la création d'un **Leaderboard** destiné à favoriser l'émulation entre les participants.
Ce projet m'a demandé de sortir des sentiers battus, car j'ai dû utiliser des outils tels que **Prometheus** et **Grafana** pour une tâche à laquelle ils ne sont pas destinés : départager les participants par ordre d'arrivée.
@ -79,23 +79,23 @@ Comment j'ai calibré les bonus et accélérateurs pour favoriser la compétitio
Tout est expliqué dans ces deux articles :
1. {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md" >}}
2. {{< internalLink path="/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md" >}}
1. [](/blog/behind-the-scenes-at-open-code-quest-how-i-designed-leaderboard/index.md)
2. [](/blog/behind-the-scenes-at-open-code-quest-how-i-implemented-leaderboard-with-acm/index.md)
## Démo "Mission Impossible" : Lego, AI & Edge Computing
Une partie de la journée, j'étais sur le stand RHEL, accompagné de [Adrien](https://www.linkedin.com/in/adrien-legros-78674a133/), [Mourad](https://www.linkedin.com/in/mourad-ouachani-0734218/) et [Pauline](https://www.linkedin.com/in/trg-pauline/) pour installer la démo "Mission Impossible" et répondre aux questions du public.
Cette démo, nous l'avons conçue pour l'événement {{< internalLink path="/speaking/platform-day-2024/index.md" >}} sur le thème du dernier opus du film **Mission Impossible: Dead Reckoning**.
Cette démo, nous l'avons conçue pour l'événement [](/speaking/platform-day-2024/index.md) sur le thème du dernier opus du film **Mission Impossible: Dead Reckoning**.
Dans cette démo, **Ethan Hunt** a besoin d'aide pour arrêter le train **Lego City #60337** avant qu'il ne soit trop tard !
Rien de moins que le sort de l'humanité est en jeu !
{{< attachedFigure src="mission-impossible-plot.png" >}}
![](mission-impossible-plot.png)
Le scénario nécessite que **Ethan Hunt** monte à bord du train pour y connecter une carte **Nvidia Jetson Orin Nano** au réseau informatique du train et y déploie une IA qui reconnaitra les panneaux de signalisation et arrêtera le train à temps avant qu'il ne déraille !
Une console permettra d'avoir une vue déportée de la caméra de vidéo surveillance du train, avec les résultats de l'inférence du modèle d'IA incrustés.
{{< attachedFigure src="mission-impossible-scenario.png" >}}
![](mission-impossible-scenario.png)
Pour mettre en oeuvre cette démo, nous avons équipé le train **Lego** d'une carte **Nvidia Jetson Orin Nano**, d'une webcam et d'une batterie portable.
La carte Nvidia Jetson Orin est un System On Chip (SoC), elle comprend tout le matériel dont **Ethan Hunt** a besoin pour sa mission : CPU, RAM, stockage...
@ -103,7 +103,7 @@ Ainsi qu'un un GPU pour accélérer les calculs !
Le Jetson reçoit le flux vidéo de la caméra embarquée et transmet les ordres au Hub **Lego** via le protocole **Bluetooth Low Energy**.
Il est alimenté via une batterie portable pour la durée de la mission.
{{< attachedFigure src="rhel-booth-mission-impossible-demo.jpeg" >}}
![](rhel-booth-mission-impossible-demo.jpeg)
Nous sommes dans un contexte de Edge Computing.
Sur le Jetson, nous avons installé **Red Hat Device Edge**.
@ -118,7 +118,7 @@ Le flux vidéo est relayé depuis le Jetson au travers d’un **broker Kafka** !
Il faut ajouter à cela des pipelines MLops pour entraîner le modèle d’IA.
Et enfin des pipelines CI/CD pour construire les images de conteneur de nos micro-services pour les architectures x86 et ARM.
{{< attachedFigure src="mission-impossible-hardware-architecture.png" >}}
![](mission-impossible-hardware-architecture.png)
Pour permettre à **Ethan Hunt** de mener à bien sa mission, il a fallu garantir la transmission de la donnée de bout en bout.
Pour cela, nous avons implémenté cinq services qui communiquent via un système d’envoi de messages asynchrone (**MQTT**).
@ -128,7 +128,7 @@ Chaque image est redimensionnée en 600x400 pixels et encapsulée dans un évén
Cet événement est transmis au modèle d'IA qui l’enrichit avec le résultat de la prédiction.
Ce dernier est transmis à un service de transformation qui a pour rôle d'extraire l'action du train, la transmettre au contrôleur de train pour ralentir ou stopper le train et en parallèle envoyer l'événement au service de streaming (**Kafka**) déployé sur un Openshift distant, qui affiche en temps réel, les images et la prédiction.
{{< attachedFigure src="mission-impossible-software-architecture.png" >}}
![](mission-impossible-software-architecture.png)
Et enfin, il nous a fallu construire un modèle d’intelligence artificielle.
Pour cela, nous avons suivi les bonnes pratiques pour gérer le cycle de vie du modèle, c’est ce qu’on appelle le **MLOps** :
@ -141,9 +141,9 @@ Pour cela, nous avons suivi les bonnes pratiques pour gérer le cycle de vie du
- **Mesurer les performances et ré-entraîner** : En observer le comportement du modèle, nous avons pu mesurer la qualité des prédictions et constater que tous les panneaux **Lego** n'était pas bien reconnus.
Nous avons pris la décision de réentrainer le modèle en l’affinant avec un jeu de données enrichi.
{{< attachedFigure src="mission-impossible-ai.png" >}}
![](mission-impossible-ai.png)
Si vous n'avez pas pu venir nous voir sur le stand, je vous propose une session de rattrapage dans la vidéo ci-dessous (capturée lors du {{< internalLink path="/speaking/platform-day-2024/index.md" >}}).
Si vous n'avez pas pu venir nous voir sur le stand, je vous propose une session de rattrapage dans la vidéo ci-dessous (capturée lors du [](/speaking/platform-day-2024/index.md)).
On y voit le train s'arrêter lorsqu'il détecte le panneau de signalisation correspondant.
{{< embeddedVideo src="mission-impossible-demo.mp4" autoplay="true" loop="true" muted="true" width="1920" height="1080" >}}

2
content/french/speaking/redhat-summit-connect-2021/index.md

@ -11,5 +11,5 @@ resources:
J'ai eu l'occasion de participer au salon [Red Hat Summit Connect 2021](https://www.redhat.com/fr/summit/connect/emea/paris) pour co-présenter la solution [Red Hat Advanced Cluster Security](https://www.redhat.com/fr/technologies/management/advanced-cluster-management) avec mon collègue [François Duthilleul](https://www.linkedin.com/posts/fduthilleul_redhat-summitconnect-paris-activity-6865998968166354944-QPlI).
{{< screenshotOf src="tweet-YadaYac.png" href="https://twitter.com/YadaYac/status/1461043727951093762" >}}
[![Tweet de @YadaYac](tweet-YadaYac.png)](https://twitter.com/YadaYac/status/1461043727951093762)

10
content/french/speaking/riviera-dev-2024/index.md

@ -22,7 +22,7 @@ L'énoncé de l'atelier est basé sur le scénario du 7ème opus de la saga **Mi
**Ethan Hunt** a besoin d'aide pour arrêter le train.
Grâce un modèle d'intelligence artificielle conçu dans **OpenShift AI** et déployé sur un **Nvidia Jetson Orin** faisant tourner **Red Hat Device Edge**, le train reconnait les panneaux de signalisation et s'arrête tout seul !
{{< attachedFigure src="train-lego-table-1.webp" title="Les participants ont dû résoudre une série d'exercice avec un train Lego sur le thème de Mission Impossible." >}}
![Les participants ont dû résoudre une série d'exercice avec un train Lego sur le thème de Mission Impossible.](train-lego-table-1.webp "Les participants ont dû résoudre une série d'exercice avec un train Lego sur le thème de Mission Impossible.")
[L'énoncé de l'atelier](https://rivieradev2024-crazytrain.netlify.app/) est composé de trois parties :
@ -46,7 +46,7 @@ Pour le participants, ça avait l'effet de bord positif de ne requérir aucun pr
Des fichiers vidéos sont stockés dans Git et sont utilisés à la place de la webcam.
La communication avec le Hub Lego est remplaçée par l'affichage des ordres qui aurait été envoyés si le Hub était présent.
{{< attachedFigure src="train-lego-table-2.webp" title="Tous les participants sont concentrés sur la réalisation des exercices." >}}
![Tous les participants sont concentrés sur la réalisation des exercices.](train-lego-table-2.webp "Tous les participants sont concentrés sur la réalisation des exercices.")
Enfin, la partie DevOps était composé de deux étapes :
@ -57,7 +57,7 @@ Si l'atelier s'est globalement bien passé, nous avons eu la surprise de constat
Le symptôme que nous avions était une WebSocket connectée mais vide.
Après investigation, il semblerait que le problème soit lié à un logiciel de sécurité installé sur les machines des participants.
{{< attachedFigure src="aide-ponctuelle.webp" title="Le problème de Web Socket a nécessité de la patience pour les participants et les intervenants Red Hat." >}}
![Le problème de Web Socket a nécessité de la patience pour les participants et les intervenants Red Hat.](aide-ponctuelle.webp "Le problème de Web Socket a nécessité de la patience pour les participants et les intervenants Red Hat.")
L'atelier s'est terminé avec un Quizz de connaissances et le vainqueur s'est vu remettre un [set Lego #60337](https://www.lego.com/fr-fr/product/express-passenger-train-60337) (le même train que celui de notre démo).
@ -65,7 +65,7 @@ Participer à cet atelier lors du Riviera Dev 2024 a été une expérience enric
Non seulement nous avons pu pratiquer sur deux sujets du moment : Edge Computing et Intelligence Artificielle, mais nous avons également offert aux participants une approche ludique à travers le prisme de la saga Mission Impossible.
Les défis rencontrés, notamment les soucis de lecture du flux vidéo, ont ajouté un peu de piment et d'émulation collective à l'exercice.
{{< attachedFigure src="groupe.webp" title="Le vainqueur du Quizz était visiblement content de l'atelier. 😅" >}}
![Le vainqueur du Quizz était visiblement content de l'atelier. 😅](groupe.webp "Le vainqueur du Quizz était visiblement content de l'atelier. 😅")
Au-delà des compétences techniques acquises, l'enthousiasme et la bonne humeur des participants ont fait de cet atelier un moment de partage et d'apprentissage.
Le quizz final a apporté une touche de compétition amicale et le prix (un set [Lego City #60337](https://www.lego.com/fr-fr/product/express-passenger-train-60337)) décerné au vainqueur a visiblement rendu ce dernier très heureux !
@ -75,6 +75,6 @@ Merci à tous les participants pour leur engagement et leur curiosité, et à l'
En dehors de l'atelier, il y a eu également une forte affluence sur le stand Red Hat où nous avions installé le train et ses rails.
Ce fût l'occasion d'échanger sur les projets Edge et IA en cours ainsi que sur les technologies utilisées.
{{< attachedFigure src="stand-redhat-1.webp" title="Le train était installé sur le stand Red Hat et a provoqué de nombreuses discussions." >}}
![Le train était installé sur le stand Red Hat et a provoqué de nombreuses discussions.](stand-redhat-1.webp "Le train était installé sur le stand Red Hat et a provoqué de nombreuses discussions.")
A l'année prochaine !

2
content/french/speaking/worldline-tech-forum-explore-france-2024/index.md

@ -12,7 +12,7 @@ topics:
[Le 18 juin 2024](https://www.linkedin.com/posts/red-hat_worldline-techatworldline-worldlinetex-activity-7209161785776451584-7-Hx?utm_source=share&utm_medium=member_desktop), Seclin a accueilli un événement technologique majeur : le [Worldline Tech Forum eXplore France 2024](https://www.linkedin.com/posts/worldlineglobal_worldline-tech-forum-explore-france-2024-activity-7218612563469578240-fTZf/).
Cet événement a rassemblé des employés Worldline et des partenaires technologiques (dont Red Hat), tous animés par une même volonté de partager les dernières innovations et leurs connaissances.
Parmi les nombreuses animations proposées lors de cette journée, notre stand Red Hat a fait sensation, notamment grâce à une démonstration originale qui a attiré de nombreux visiteurs : le désormais bien connu train Lego *Mission Impossible*, que nous avons présenté lors du {{< internalLink path="/speaking/platform-day-2024/index.md" >}}.
Parmi les nombreuses animations proposées lors de cette journée, notre stand Red Hat a fait sensation, notamment grâce à une démonstration originale qui a attiré de nombreux visiteurs : le désormais bien connu train Lego *Mission Impossible*, que nous avons présenté lors du [](/speaking/platform-day-2024/index.md).
## Une expérience immersive et ludique avec le train Lego

24
layouts/_markup/render-codeblock.html

@ -0,0 +1,24 @@
{{- /*
-- Renders the fenced code blocks, optionally prefixed by a file name or a
-- title:
--
-- ```yaml {filename="config.yaml" hl_lines="3-5"}
-- ```sh {title="CoreOS"}
*/ -}}
{{- $result := transform.HighlightCodeBlock . -}}
{{- $label := "" -}}
{{- with .Attributes.filename -}}
{{- $label = printf (i18n "file") . -}}
{{- else -}}
{{- with .Attributes.title -}}{{- $label = . -}}{{- end -}}
{{- end -}}
{{- /* The filename and title attributes must not be rendered on the wrapper */ -}}
{{- $highlighted := replaceRE `^<div class="highlight"[^>]*>` `<div class="highlight">` (string $result.Wrapped) 1 | safeHTML -}}
{{- if $label -}}
<div class="highlightedFile">
<div class="filename">{{ $label }}</div>
{{- $highlighted -}}
</div>
{{- else -}}
{{- $highlighted -}}
{{- end -}}

32
layouts/_markup/render-image.html

@ -0,0 +1,32 @@
{{- /*
-- Renders the Markdown images.
--
-- The image is resolved to a resource of the current page, or to a global
-- resource (assets, static/images). A standalone image is rendered as a
-- figure, with its title as caption (Markdown allowed):
--
-- ![Alternative text](image.png "Caption")
*/ -}}
{{- $u := urls.Parse .Destination -}}
{{- $src := .Destination -}}
{{- if not $u.IsAbs -}}
{{- with or (.Page.Resources.Get $u.Path) (resources.Get (strings.TrimPrefix "/" $u.Path)) -}}
{{- $src = .RelPermalink -}}
{{- else -}}
{{- if not (strings.HasPrefix $u.Path "/") -}}
{{- errorf "%s: image %q not found" .Page.String .Destination -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if .IsBlock -}}
<figure>
<img src="{{ $src }}" alt="{{ .PlainText }}">
{{- with .Title }}
<figcaption>
{{- . | markdownify -}}
</figcaption>
{{- end }}
</figure>
{{- else -}}
<img src="{{ $src }}" alt="{{ .PlainText }}">
{{- end -}}

64
layouts/_markup/render-link.html

@ -0,0 +1,64 @@
{{- /*
-- Renders the Markdown links.
--
-- Internal links are resolved, in this order:
-- 1. to a resource of the current page (attached file), which is then
-- published: [the slides](slides.pdf)
-- 2. to a page of the current site, then of the other site (language).
-- A flag is displayed when the target page is in a different language
-- than the current page. When the link text is empty, the title of the
-- target page is used: [](/blog/my-article.md)
-- 3. relative links (../other-article/) are made relative to the site root,
-- so that they also work in the RSS feeds.
--
-- The output must not contain any extra whitespace since the link is
-- usually embedded in a sentence.
*/ -}}
{{- $u := urls.Parse .Destination -}}
{{- $href := .Destination -}}
{{- /*
-- An image wrapped in a link ([![alt](image.png)](URL)) is rendered as a
-- figure by the image render hook: a figure is not allowed in a link, so
-- only the image is kept.
*/ -}}
{{- $text := replaceRE `^\s*<figure>\s*(<img[^>]*>)\s*</figure>\s*$` "$1" .Text -}}
{{- $flag := "" -}}
{{- /* Links whose destination is a shortcode are left as-is (placeholder) */ -}}
{{- if and (not $u.IsAbs) $u.Path (not (strings.Contains .Destination "HAHAHUGOSHORTCODE")) -}}
{{- $fragment := cond (ne $u.Fragment "") (printf "#%s" $u.Fragment) "" -}}
{{- $currentPage := .Page -}}
{{- $currentLang := .Page.Lang -}}
{{- /* 1. Resources of the current page (attached files) */ -}}
{{- with .Page.Resources.Get $u.Path -}}
{{- $href = printf "%s%s" .RelPermalink $fragment -}}
{{- else -}}
{{- /* 2. Pages of the current site first, then of the other site */ -}}
{{- $target := $currentPage.GetPage $u.Path -}}
{{- if and (not $target) (strings.HasPrefix $u.Path "/") -}}
{{- range hugo.Sites -}}
{{- if and (not $target) (ne .Language.Lang $currentLang) -}}
{{- with .GetPage $u.Path -}}{{- $target = . -}}{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if $target -}}
{{- $href = printf "%s%s" $target.RelPermalink $fragment -}}
{{- if not $text -}}{{- $text = $target.Title | htmlEscape -}}{{- end -}}
{{- if and (eq $currentLang "en") (eq $target.Lang "fr") -}}
{{- $flag = partial "icons/fr.svg" $target -}}
{{- else if eq $currentLang "fr" -}}
{{- partial "lang-detection.html" $target -}}
{{- if $target.Store.Get "en_article_in_fr_site" -}}
{{- $flag = partial "icons/en.svg" $target -}}
{{- end -}}
{{- end -}}
{{- else if not (strings.HasPrefix $u.Path "/") -}}
{{- /* 3. Relative links */ -}}
{{- $href = printf "%s%s%s" (path.Join $currentPage.RelPermalink $u.Path) (cond (strings.HasSuffix $u.Path "/") "/" "") $fragment -}}
{{- end -}}
{{- end -}}
{{- end -}}
<a href="{{ $href | safeURL }}"{{ with .Title }} title="{{ . }}"{{ end }}>{{ $text | safeHTML }}</a>
{{- with $flag }} {{ . | strings.TrimSpace | safeHTML }}{{ end -}}

2
layouts/_partials/article-headline.html

@ -7,5 +7,5 @@
{{ end }}
</a></h4>
{{- partial "post-metadata.html" . -}}
<p>{{ .current.Summary | plainify | htmlUnescape }} <a href="{{ .current.RelPermalink }}" class="continue-reading">{{ i18n "continue_reading" }}</a></p>
<p>{{ .current.Summary | replaceRE `(?s)\s*<svg.*?</svg>` "" | plainify | htmlUnescape }} <a href="{{ .current.RelPermalink }}" class="continue-reading">{{ i18n "continue_reading" }}</a></p>
</section>

2
layouts/_partials/page-description.html

@ -18,7 +18,7 @@
{{- else if eq .Kind "term" -}}
{{- $description = printf "%s %s" (i18n "articles_related_to" | strings.TrimSpace) .Title -}}
{{- else -}}
{{- $summary := .Summary | plainify | htmlUnescape | replaceRE `\s+` " " | strings.TrimSpace -}}
{{- $summary := .Summary | replaceRE `(?s)\s*<svg.*?</svg>` "" | plainify | htmlUnescape | replaceRE `\s+` " " | strings.TrimSpace -}}
{{- with $summary -}}
{{- $description = truncate 160 . -}}
{{- else -}}

2
layouts/search.searchindex.json

@ -29,6 +29,6 @@
{{- .Store.Set "en_article_in_fr_site" false -}}
{{- partial "lang-detection.html" . -}}
{{- $.Store.Add "index" (dict "title" .Title "opensource" .Params.opensource "topics" .Params.topics "permalink" .Permalink "hasEnFlag" (.Store.Get "en_article_in_fr_site") "type" .Type "type_label" (i18n (print "type_" .Type)) "date" ($.Store.Get "date") "reading_time" ($.Store.Get "reading_time") "summary" (.Summary | plainify | htmlUnescape)) -}}
{{- $.Store.Add "index" (dict "title" .Title "opensource" .Params.opensource "topics" .Params.topics "permalink" .Permalink "hasEnFlag" (.Store.Get "en_article_in_fr_site") "type" .Type "type_label" (i18n (print "type_" .Type)) "date" ($.Store.Get "date") "reading_time" ($.Store.Get "reading_time") "summary" (.Summary | replaceRE `(?s)\s*<svg.*?</svg>` "" | plainify | htmlUnescape)) -}}
{{- end -}}
{{- dict "taxonomies" ($.Store.Get "taxonomies") "index" ($.Store.Get "index") | jsonify -}}

135
scripts/convert-shortcodes.py

@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Converts the custom shortcodes of www.itix.fr to standard Markdown syntax,
rendered by the render hooks in layouts/_markup.
{{< attachedFigure src="x.png" title="Caption" >}} -> ![Caption](x.png "Caption")
{{< screenshotOf src="tweet-x.png" href="URL" >}} -> [![Tweet by @x](tweet-x.png)](URL)
[text]({{< attachedFileLink src="x.pdf" >}}) -> [text](x.pdf)
(kept as-is outside of a Markdown link)
[text]({{< relref "/blog/x" >}}) -> [text](/blog/x)
{{< internalLink path="/blog/x.md" >}} -> [](/blog/x.md)
{{< internalLink path="/blog/x.md" title="T" >}} -> [T](/blog/x.md)
{{< highlightFile "f.yaml" "yaml" "hl_lines=3" >}} -> ```yaml {filename="f.yaml" hl_lines="3"}
{{< highlightWithTitle "T" "sh" "" >}} -> ```sh {title="T"}
{{< highlight yaml "hl_lines=5" >}} -> ```yaml {hl_lines="5"}
Usage: scripts/convert-shortcodes.py FILE.md [FILE.md...]
"""
import re
import sys
def parse_args(args):
"""Parses shortcode arguments: named (key="value") or positional."""
named, positional = {}, []
for m in re.finditer(r'(\w+)="((?:[^"\\]|\\.)*)"|"((?:[^"\\]|\\.)*)"|(\S+)', args):
if m.group(1):
named[m.group(1)] = m.group(2).replace('\\"', '"')
elif m.group(3) is not None:
positional.append(m.group(3).replace('\\"', '"'))
else:
positional.append(m.group(4))
return named, positional
def plain(text):
"""Strips the Markdown formatting of a caption, to be used as alt text."""
text = re.sub(r'\[([^\]]*)\]\([^)]*\)', r'\1', text)
text = re.sub(r'[`*_]', '', text)
return text.strip()
def md_title(text):
return '"%s"' % text.replace('\\', '\\\\').replace('"', '\\"')
def md_alt(text):
return text.replace('[', '\\[').replace(']', '\\]')
def fence_attrs(options):
"""Converts Hugo highlight options ("hl_lines=3 5-7,linenos=true") to fence attributes."""
attrs = []
for opt in re.split(r',\s*', options or ''):
if '=' not in opt:
continue
key, value = opt.split('=', 1)
if value.strip():
attrs.append('%s=%s' % (key.strip(), md_title(value.strip())))
return attrs
def fence(lang, attrs, inner):
inner = inner.strip('\n')
ticks = '```'
while ticks in inner:
ticks += '`'
info = lang + (' {%s}' % ' '.join(attrs) if attrs else '')
return '%s%s\n%s\n%s' % (ticks, info, inner, ticks)
def standalone(match, replacement):
"""Ensures a block-level element (figure) is separated by blank lines."""
s, start, end = match.string, match.start(), match.end()
before = '' if start == 0 or s[:start].endswith('\n\n') else ('\n' if s[:start].endswith('\n') else '\n\n')
after = '' if end == len(s) or s[end:].startswith('\n\n') else ('\n' if s[end:].startswith('\n') else '\n\n')
return before + replacement + after
def convert(text, lang):
# Code blocks
def code(m):
name, args, inner = m.group(1), m.group(2), m.group(3)
named, pos = parse_args(args)
if name == 'highlightFile':
return fence(pos[1], ['filename=%s' % md_title(pos[0])] + fence_attrs(pos[2] if len(pos) > 2 else ''), inner)
if name == 'highlightWithTitle':
return fence(pos[1], ['title=%s' % md_title(pos[0])] + fence_attrs(pos[2] if len(pos) > 2 else ''), inner)
return fence(pos[0], fence_attrs(pos[1] if len(pos) > 1 else ''), inner)
text = re.sub(r'\{\{<\s*(highlightFile|highlightWithTitle|highlight)\s+(.*?)\s*>\}\}(.*?)\{\{<\s*/\s*\1\s*>\}\}',
code, text, flags=re.S)
# Figures
def figure(m):
named, _ = parse_args(m.group(1))
title = named.get('title', '')
img = '![%s](%s%s)' % (md_alt(plain(title)), named['src'], ' ' + md_title(title) if title else '')
return standalone(m, img)
text = re.sub(r'\{\{<\s*attachedFigure\s+(.*?)\s*>\}\}', figure, text)
# Only one blank line between consecutive figures
text = re.sub(r'(!\[[^\n]*\))\n{3,}(?=!\[)', r'\1\n\n', text)
# Screenshots of tweets, linked to the tweet
def screenshot(m):
named, _ = parse_args(m.group(1))
handle = re.sub(r'^tweet-|(-\d+)?\.\w+$', '', named['src'])
alt = ('Tweet de @%s' if lang == 'fr' else 'Tweet by @%s') % handle
return '[![%s](%s)](%s)' % (alt, named['src'], named['href'])
text = re.sub(r'\{\{<\s*screenshotOf\s+(.*?)\s*>\}\}', screenshot, text)
# Links
# (attachedFileLink is kept when used elsewhere, e.g. in code blocks)
text = re.sub(r'\]\(\{\{<\s*attachedFileLink\s+src="([^"]+)"\s*>\}\}\)', r'](\1)', text)
text = re.sub(r'\{\{<\s*relref\s+"([^"]+)"\s*>\}\}', r'\1', text)
def internal_link(m):
named, _ = parse_args(m.group(1))
return '[%s](%s)' % (named.get('title', ''), named['path'])
text = re.sub(r'\{\{<\s*internalLink\s+(.*?)\s*>\}\}', internal_link, text)
return text
for filename in sys.argv[1:]:
with open(filename, encoding='utf-8') as f:
original = f.read()
converted = convert(original, 'fr' if '/french/' in filename else 'en')
if converted != original:
with open(filename, 'w', encoding='utf-8') as f:
f.write(converted)
print('converted:', filename)
Loading…
Cancel
Save