Merge branch 'main' into 2021-02-enix

This commit is contained in:
Jerome Petazzoni
2021-02-24 22:35:35 +01:00
10 changed files with 268 additions and 118 deletions
+2
View File
@@ -1 +1,3 @@
INFRACLASS=scaleway
#SCW_INSTANCE_TYPE=DEV1-L
#SCW_ZONE=fr-par-2
+7 -7
View File
@@ -5,6 +5,9 @@ if ! [ -f ~/.config/scw/config.yaml ]; then
warn "~/.config/scw/config.yaml not found."
fi
SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M}
SCW_ZONE=${SCW_ZONE-fr-par-1}
infra_list() {
scw instance server list -o json |
jq -r '.[] | [.id, .name, .state, .commercial_type] | @tsv'
@@ -13,9 +16,6 @@ infra_list() {
infra_start() {
COUNT=$1
SCW_INSTANCE_TYPE=${SCW_INSTANCE_TYPE-DEV1-M}
SCW_ZONE=${SCW_ZONE-fr-par-1}
for I in $(seq 1 $COUNT); do
NAME=$(printf "%s-%03d" $TAG $I)
sep "Starting instance $I/$COUNT"
@@ -36,16 +36,16 @@ infra_stop() {
scw_get_ids_by_tag $TAG | wc -l
info "Deleting instances..."
scw_get_ids_by_tag $TAG |
xargs -n1 -P10 -I@@ \
scw instance server delete force-shutdown=true server-id=@@
xargs -n1 -P10 \
scw instance server delete zone=${SCW_ZONE} force-shutdown=true with-ip=true
}
scw_get_ids_by_tag() {
TAG=$1
scw instance server list name=$TAG -o json | jq -r .[].id
scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].id
}
scw_get_ips_by_tag() {
TAG=$1
scw instance server list name=$TAG -o json | jq -r .[].public_ip.address
scw instance server list zone=${SCW_ZONE} name=$TAG -o json | jq -r .[].public_ip.address
}
+46 -48
View File
@@ -2,11 +2,11 @@
"""
There are two ways to use this script:
1. Pass a tag name as a single argument.
It will then take the clusters corresponding to that tag, and assign one
domain name per cluster. Currently it gets the domains from a hard-coded
path. There should be more domains than clusters.
Example: ./map-dns.py 2020-08-15-jp
1. Pass a file name and a tag name as a single argument.
It will load a list of domains from the given file (one per line),
and assign them to the clusters corresponding to that tag.
There should be more domains than clusters.
Example: ./map-dns.py domains.txt 2020-08-15-jp
2. Pass a domain as the 1st argument, and IP addresses then.
It will configure the domain with the listed IP addresses.
@@ -19,55 +19,53 @@ import requests
import sys
import yaml
# configurable stuff
domains_file = "../../plentydomains/domains.txt"
# This can be tweaked if necessary.
config_file = os.path.join(
os.environ["HOME"], ".config/gandi/config.yaml")
tag = None
os.environ["HOME"], ".config/gandi/config.yaml")
apiurl = "https://dns.api.gandi.net/api/v5/domains"
if len(sys.argv) == 2:
tag = sys.argv[1]
domains = open(domains_file).read().split()
domains = [ d for d in domains if not d.startswith('#') ]
ips = open(f"tags/{tag}/ips.txt").read().split()
settings_file = f"tags/{tag}/settings.yaml"
clustersize = yaml.safe_load(open(settings_file))["clustersize"]
else:
domains = [sys.argv[1]]
ips = sys.argv[2:]
clustersize = len(ips)
# inferred stuff
apikey = yaml.safe_load(open(config_file))["apirest"]["key"]
# now do the fucking work
while domains and ips:
domain = domains[0]
domains = domains[1:]
cluster = ips[:clustersize]
ips = ips[clustersize:]
print(f"{domain} => {cluster}")
zone = ""
node = 0
for ip in cluster:
node += 1
zone += f"@ 300 IN A {ip}\n"
zone += f"* 300 IN A {ip}\n"
zone += f"node{node} 300 IN A {ip}\n"
r = requests.put(
f"{apiurl}/{domain}/records",
headers={"x-api-key": apikey},
data=zone)
print(r.text)
# Figure out if we're called for a bunch of domains, or just one.
first_arg = sys.argv[1]
if os.path.isfile(first_arg):
domains = open(first_arg).read().split()
domains = [ d for d in domains if not d.startswith('#') ]
tag = sys.argv[2]
ips = open(f"tags/{tag}/ips.txt").read().split()
settings_file = f"tags/{tag}/settings.yaml"
clustersize = yaml.safe_load(open(settings_file))["clustersize"]
else:
domains = [first_arg]
ips = sys.argv[2:]
clustersize = len(ips)
#r = requests.get(
# f"{apiurl}/{domain}/records",
# headers={"x-api-key": apikey},
# )
# Now, do the work.
while domains and ips:
domain = domains[0]
domains = domains[1:]
cluster = ips[:clustersize]
ips = ips[clustersize:]
print(f"{domain} => {cluster}")
zone = ""
node = 0
for ip in cluster:
node += 1
zone += f"@ 300 IN A {ip}\n"
zone += f"* 300 IN A {ip}\n"
zone += f"node{node} 300 IN A {ip}\n"
r = requests.put(
f"{apiurl}/{domain}/records",
headers={"x-api-key": apikey},
data=zone)
print(r.text)
#r = requests.get(
# f"{apiurl}/{domain}/records",
# headers={"x-api-key": apikey},
# )
if domains:
print(f"Good, we have {len(domains)} domains left.")
print(f"Good, we have {len(domains)} domains left.")
if ips:
print(f"Crap, we have {len(ips)} IP addresses left.")
print(f"Crap, we have {len(ips)} IP addresses left.")
+17 -9
View File
@@ -58,27 +58,24 @@
*probably aggregation layer*
---
## How are resources organized?
- Let's have a look at the Kubernetes API hierarchical structure
- Useful: `.metadata.selfLink` contains the URI of a resource
- We'll ask `kubectl` to show us the exacts requests that it's making
.exercise[
- Check the `apiVersion` and URI of a "core" resource, e.g. a Node:
- Check the URI for a cluster-scope, "core" resource, e.g. a Node:
```bash
kubectl get nodes -o json | jq .items[0].apiVersion
kubectl get nodes -o json | jq .items[0].metadata.selfLink
kubectl -v6 get node node1
```
- Get the `apiVersion` and URI for a "non-core" resource, e.g. a ClusterRole:
- Check the URI for a cluster-scope, "non-core" resource, e.g. a ClusterRole:
```bash
kubectl get clusterrole view -o json | jq .apiVersion
kubectl get clusterrole view -o json | jq .metadata.selfLink
kubectl -v6 get clusterrole view
```
]
@@ -123,6 +120,17 @@ class: extra-details
## Namespaced resources
- What about namespaced resources?
.exercise[
- Check the URI for a namespaced, "core" resource, e.g. a Service:
```bash
kubectl -v6 get service kubernetes --namespace default
```
]
- Here are what namespaced resources URIs look like:
```
@@ -168,7 +176,7 @@ class: extra-details
kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy
PODNAME=$(
kubectl get pods --namespace=kube-system --selector=k8s-app=kube-proxy \
-o json | jq .items[0].metadata.name)
-o json | jq -r .items[0].metadata.name)
```
- Execute a command in a pod, showing the API requests:
+30 -10
View File
@@ -60,21 +60,41 @@
## Command-line arguments
- Pass options to `args` array in the container specification
- Indicate what should run in the container
- Example ([source](https://github.com/coreos/pods/blob/master/kubernetes.yaml#L29)):
- Pass `command` and/or `args` in the container options in a Pod's template
- Both `command` and `args` are arrays
- Example ([source](https://github.com/jpetazzo/container.training/blob/main/k8s/consul-1.yaml#L70)):
```yaml
args:
- "--data-dir=/var/lib/etcd"
- "--advertise-client-urls=http://127.0.0.1:2379"
- "--listen-client-urls=http://127.0.0.1:2379"
- "--listen-peer-urls=http://127.0.0.1:2380"
- "--name=etcd"
args:
- "agent"
- "-bootstrap-expect=3"
- "-retry-join=provider=k8s label_selector=\"app=consul\" namespace=\"$(NS)\""
- "-client=0.0.0.0"
- "-data-dir=/consul/data"
- "-server"
- "-ui"
```
- The options can be passed directly to the program that we run ...
---
... or to a wrapper script that will use them to e.g. generate a config file
## `args` or `command`?
- Use `command` to override the `ENTRYPOINT` defined in the image
- Use `args` to keep the `ENTRYPOINT` defined in the image
(the parameters specified in `args` are added to the `ENTRYPOINT`)
- In doubt, use `command`
- It is also possible to use *both* `command` and `args`
(they will be strung together, just like `ENTRYPOINT` and `CMD`)
- See the [docs](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes) to see how they interact together
---
+5 -13
View File
@@ -10,7 +10,7 @@ Level 2: make it so that the number of replicas can be set with `--set replicas=
Level 3: change the colors of the lego bricks.
(For level 3, fork the repository and use ctr.run to build images.)
(For level 3, you'll have to build/push your own images.)
See next slide if you need hints!
@@ -44,20 +44,12 @@ Also add `replicas: 5` to `values.yaml` to provide a default value.
## Changing the color
- Fork the repository
- Create an account on e.g. Docker Hub (e.g. `janedoe`)
- Make sure that your fork has valid Dockerfiles
(or identify a branch that has valid Dockerfiles)
- Use the following images:
ctr.run/yourgithubusername/wordsmith/db:branchname
(replace db with web and words for the other components)
- Create an image repository (e.g. `janedoe/web`)
- Change the images and/or CSS in `web/static`
- Commit, push, trigger a rolling update
- Build and push
(`imagePullPolicy` should be `Always`, which is the default)
- Trigger a rolling update using the image you just pushed
+17 -1
View File
@@ -244,7 +244,7 @@ fine for personal and development clusters.)
- Add the `stable` repo:
```bash
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
helm repo add stable https://charts.helm.sh/stable
```
]
@@ -255,6 +255,22 @@ It's OK to add a repo that already exists (it will merely update it).
---
class: extra-details
## Deprecation warning
- That "stable" is being deprecated, in favor of a more decentralized approach
(each community / company / group / project hosting their own repository)
- We're going to use it here for educational purposes
- But if you're looking for production-grade charts, look elsewhere!
(namely, on the Helm Hub)
---
## Search available charts
- We can search available charts with `helm search`
+17 -17
View File
@@ -72,7 +72,7 @@
- Deploy DockerCoins, and scale up the `worker` Deployment:
```bash
kubectl apply -f ~/container.training/k8/dockercoins.yaml
kubectl apply -f ~/container.training/k8s/dockercoins.yaml
kubectl scale deployment worker --replicas=10
```
@@ -118,7 +118,7 @@
- Deploy `httplat`:
```bash
kubectl create deployment httplat -- httplat http://rng/
kubectl create deployment httplat --image=jpetazzo/httplat -- httplat http://rng/
```
- Expose it:
@@ -512,20 +512,20 @@ no custom metrics API (custom.metrics.k8s.io) registered
Here is the rule that we need to add to the configuration:
```yaml
- seriesQuery: |
httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""}
resources:
overrides:
kubernetes_namespace:
resource: namespace
kubernetes_name:
resource: service
name:
matches: "httplat_latency_seconds_sum"
as: "httplat_latency_seconds"
metricsQuery: |
rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m])
/rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m])
- seriesQuery: |
httplat_latency_seconds_sum{kubernetes_namespace!="",kubernetes_name!=""}
resources:
overrides:
kubernetes_namespace:
resource: namespace
kubernetes_name:
resource: service
name:
matches: "httplat_latency_seconds_sum"
as: "httplat_latency_seconds"
metricsQuery: |
rate(httplat_latency_seconds_sum{<<.LabelMatchers>>}[2m])
/rate(httplat_latency_seconds_count{<<.LabelMatchers>>}[2m])
```
(I built it following the [walkthrough](https://github.com/DirectXMan12/k8s-prometheus-adapter/blob/master/docs/config-walkthrough.md
@@ -636,7 +636,7 @@ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1
Check that our `httplat` metrics are available:
```bash
kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1\
/namespaces/coins/services/httplat/httplat_latency_seconds
/namespaces/customscaling/services/httplat/httplat_latency_seconds
```
Also check the logs of the `prometheus-adapter` and the `kube-controller-manager`.
+122
View File
@@ -68,6 +68,128 @@
“Ah yes, this secret is a ...”
---
## Accessing private repositories
- Let's see how to access an image on private registry!
- These images are protected by a username + password
(on some registries, it's token + password, but it's the same thing)
- To access a private image, we need to:
- create a secret
- reference that secret in a Pod template
- or reference that secret in a ServiceAccount used by a Pod
---
## In practice
- Let's try to access an image on a private registry!
- image = docker-registry.enix.io/jpetazzo/private:latest
- user = reader
- password = VmQvqdtXFwXfyy4Jb5DR
.exercise[
- Create a Deployment using that image:
```bash
kubectl create deployment priv \
--image=docker-registry.enix.io/jpetazzo/private
```
- Check that the Pod won't start:
```bash
kubectl get pods --selector=app=priv
```
]
---
## Creating a secret
- Let's create a secret with the information provided earlier
.exercise[
- Create the registry secret:
```bash
kubectl create secret docker-registry enix \
--docker-server=docker-registry.enix.io \
--docker-username=reader \
--docker-password=VmQvqdtXFwXfyy4Jb5DR
```
]
Why do we have to specify the registry address?
If we use multiple sets of credentials for different registries, it prevents leaking the credentials of one registry to *another* registry.
---
## Using the secret
- The first way to use a secret is to add it to `imagePullSecrets`
(in the `spec` section of a Pod template)
.exercise[
- Patch the `priv` Deployment that we created earlier:
```bash
kubectl patch deploy priv --patch='
spec:
template:
spec:
imagePullSecrets:
- name: enix
'
```
]
---
## Checking the results
.exercise[
- Confirm that our Pod can now start correctly:
```bash
kubectl get pods --selector=app=priv
```
]
---
## Another way to use the secret
- We can add the secret to the ServiceAccount
- This is convenient to automatically use credentials for *all* pods
(as long as they're using a specific ServiceAccount, of course)
.exercise[
- Add the secret to the ServiceAccount:
```bash
kubectl patch serviceaccount default --patch='
imagePullSecrets:
- name: enix
'
```
]
---
+5 -13
View File
@@ -94,28 +94,20 @@
## Building on the fly
- Some services can build images on the fly from a repository
- Conceptually, it is possible to build images on the fly from a repository
- Example: [ctr.run](https://ctr.run/)
.exercise[
(deprecated in August 2020, after being aquired by Datadog)
- Use ctr.run to automatically build a container image and run it:
- It did allow something like this:
```bash
docker run ctr.run/github.com/jpetazzo/container.training/dockercoins/hasher
```
<!--
```longwait Sinatra```
```key ^C```
-->
- No alternative yet
]
There might be a long pause before the first layer is pulled,
because the API behind `docker pull` doesn't allow to stream build logs, and there is no feedback during the build.
It is possible to view the build logs by setting up an account on [ctr.run](https://ctr.run/).
(free startup idea, anyone?)
???