This commit is contained in:
2022-02-25 18:27:08 +01:00
parent a02c9a5279
commit 8b085f5162
8 changed files with 213 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -164,6 +164,14 @@ Note as the blobs of image is always physically in the disk, even when "deleted"
For that execute `registry garbage-collect /etc/docker/registry/config.yml` inside docker registry.
{{< /alert >}}
### Register registry in Portainer
For our future app deployments from our built docker image, we need to register our new private registry with proper credentials. Go to *Registries* menu of Portainer, and create the new registry.
[![Portainer registries](portainer-registries.png)](portainer-registries.png)
Save it, and you have registered repository, which will allow proper pulling from it when next stack developments.
## CI/CD with Drone 🪁
It's finally time to use our currently unused `runner-01` ! We'll use Drone as free self-hosted solution for docker image building. The all CI/CD process can be summarized to this flow chart :
@ -214,8 +222,8 @@ services:
DRONE_GITEA_CLIENT_SECRET:
DRONE_GITEA_SERVER: https://gitea.sw.okami101.io
DRONE_RPC_SECRET:
DRONE_SERVER_HOST: drone.sw.okami101.io
DRONE_SERVER_PROTO: https
DRONE_SERVER_HOST:
DRONE_SERVER_PROTO:
DRONE_USER_CREATE: username:adr1enbe4udou1n,admin:true
networks:
- traefik_public
@ -251,9 +259,11 @@ Don't forget to have proper docker labels on nodes, as explain [here]({{< ref "0
| variable | description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `DRONE_GITEA_CLIENT_ID` | Use the above client ID token |
| `DRONE_GITEA_CLIENT_SECRET` | Use the above client secret token |
| `DRONE_DATABASE_PASSWORD` | Use the database password |
| `DRONE_SERVER_HOST` | The host of main Drone server. I'll use `drone.sw.okami101.io` here. |
| `DRONE_SERVER_PROTO` | The scheme protocol, which is `https`. |
| `DRONE_GITEA_CLIENT_ID` | Use the above client ID token. |
| `DRONE_GITEA_CLIENT_SECRET` | Use the above client secret token. |
| `DRONE_DATABASE_PASSWORD` | Use the database password. |
| `DRONE_RPC_SECRET` | Necessary for proper secured authentication between Drone and runners. Use `openssl rand -hex 16` for generating a valid token. |
| `DRONE_USER_CREATE` | The initial user to create at launch. Put your Gitea username here for setting automatically Gitea user as drone administrator. |
@ -265,6 +275,204 @@ Finalize registration, and you should finally arrive to main Drone dashboard. If
[![Drone dashboard](drone-dashboard.png)](drone-dashboard.png)
{{< alert >}}
Ensure that the runner detect the remote Drone server before continue. You can check through Grafana logs for any `successfully pinged the remote server`.
{{< /alert >}}
## Test with basic project
We have all the minimal CI/CD pipeline in place ! Let's create a basic backend Dotnet API and test all CI/CD workflow !
### Create new API project ✨
Firstly, create a new `my-weather-api` private repository through Gitea. I'll use `main` as default branch here.
[![Gitea new repository](gitea-empty-repository.png)](gitea-empty-repository.png)
Next I presume you have [.NET SDK](https://dotnet.microsoft.com/en-us/download) locally installed. Create a new ASP.NET Core Web API project and push into Gitea.
```sh
dotnet new webapi -o my-weather-api --no-https
cd my-weather-api
dotnet new gitignore
git init
git add .
git commit -m "first commit"
git remote add origin git@gitea.sw.okami101.io:adr1enbe4udou1n/my-weather-api.git # if you use ssh
git push -u origin main
```
{{< alert >}}
Don't forget `--no-https` as we'll use Traefik as main SSL provider, otherwise the app will not properly respond when deployed. It will simply skip the `app.UseHttpsRedirection();` line code middleware.
{{< /alert >}}
Project should be pushed correctly :
[![Gitea new repository](gitea-new-repository.png)](gitea-new-repository.png)
### Drone configuration 🛠️
Let's now activate the repo in Drone. Click on *SYNC* button. Click on new repo and activate the repository.
[![Drone repository settings](drone-repository-settings.png)](drone-repository-settings.png)
It will create a webhook inside repository settings, triggered on every code push.
Now generate a new SSH key on `manager-01` :
```sh
ssh-keygen -t ed25519 -C "admin@sw.okami101.io"
cat .ssh/id_ed25519 # the private key to set in swarm_ssh_key
cat .ssh/id_ed25519.pub # the public key to add just below
echo "ssh-ed25519 AAAA... admin@sw.okami101.io" | tee -a .ssh/authorized_keys
```
Then configure the repository settings on Drone. Go to *Organization > Secrets* section and add some global secrets.
| name | description |
| ------------------- | ------------------------------------------ |
| `registry_username` | The username access of our docker registry |
| `registry_password` | The password access of our docker registry |
| `swarm_ssh_key` | The **private** above key |
[![Drone secrets](drone-secrets.png)](drone-secrets.png)
### Configure the pipelines (the CI part) 🏗️
For working, Drone needs a `.drone.yml` file in root of repository. This file will describe all steps of our build pipeline. Let's create and explain it :
```yml
kind: pipeline
type: docker
name: default
steps:
- name: build
image: mcr.microsoft.com/dotnet/sdk:6.0
commands:
- dotnet publish -c Release -o ./publish
- name: image
image: plugins/docker
settings:
registry: registry.sw.okami101.io
repo: registry.sw.okami101.io/adr1enbe4udou1n/my-weather-api
tags: latest
username:
from_secret: registry_username
password:
from_secret: registry_password
trigger:
event:
- push
- pull_request
```
It's just simple 2 steps :
1. `build` : Here is the step for project dependencies import, compilation, testing, and code linting / formatting. This is a very basic project here, so we start with a simple building. The image `mcr.microsoft.com/dotnet/sdk:6.0` is the required docker image for proper .NET building. The publish command will generate a `publish` subdirectory.
2. `image` : We use the [official docker plugin](https://plugins.drone.io/drone-plugins/drone-docker/) for building our final production based docker image, done from the below Dockerfile. Use `repo` as the final docker image name.
Next create the Dockerfile which will be used for `image` step :
```Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0
RUN apt-get install -y tzdata
COPY /publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "my-weather-api.dll"]
```
We use production suited .NET runtime image `mcr.microsoft.com/dotnet/aspnet:6.0`. Note as **WE MUST** do the simplest commands possible in order to have the lightest image layers, as it's the production image. All we have to do is to copy the final published binaries from above `build` drone step.
Commit both above files and push to remote repo. Drone should be automatically triggered for building and activate the runner. The runner will clone the project and process all pipeline's steps.
[![Drone build](drone-build.png)](drone-build.png)
If all's going well, the final image should be pushed in our docker registry. You can ensure it by navigating to <https://registry.sw.okami101.io>.
### Deployment (the CD part) 🚀
Our application is now ready for production deployment ! Let's create our new shiny `weather` stack :
```yml
version: "3"
services:
app:
image: registry.sw.okami101.io/adr1enbe4udou1n/my-weather-api
environment:
ASPNETCORE_ENVIRONMENT: Development
networks:
- traefik_public
deploy:
labels:
- traefik.enable=true
- traefik.http.services.my-weather-api.loadbalancer.server.port=80
placement:
constraints:
- node.labels.environment == production
networks:
traefik_public:
external: true
```
{{< alert >}}
I use `Development` in order to have the swagger UI.
Be sure to have registered the private registry in Portainer before deploying as [explained here](#register-registry-in-portainer).
{{< /alert >}}
Finally, deploy and see the result in <https://weather.sw.okami101.io/swagger>. You should access to the swagger UI, and API endpoints should correctly respond.
#### Continuous deployment
Now it's clear that we don't want to deploy manually every time when the code is pushed.
First be sure that following `docker service update --image registry.sw.okami101.io/adr1enbe4udou1n/my-weather-api:latest weather_app --with-registry-auth` command works well in `manager-01`. It's simply update the current `weather_app` service with the last available image version from the private registry.
Now we must be sure that the `runner-01` host can reach the `manager-01` server from outside. If you have applied the firewall at the beginning of this tutorial, only our own IP is authorized. Let's add the public IP of `runner-01` to your `firewall-external` inside Hetzner console.
Now let's add a new `deploy` step inside `.drone.yml` into our pipeline for automatic deployment !
```yml
#...
- name: deploy
image: appleboy/drone-ssh
settings:
host: sw.okami101.io
port: 2222
username: swarm
key:
from_secret: swarm_ssh_key
script:
- docker service update --image registry.sw.okami101.io/adr1enbe4udou1n/my-weather-api:latest weather_app --with-registry-auth
#...
```
The as example edit `Program.cs` file and change next line :
```cs
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Weather API",
Version = "0.0.1",
});
});
```
Push and back to your API, and the title and version should be automatically updated !
[![Weather API](weather-api.png)](weather-api.png)
## SonarQube 📈
## Tracing with Jaeger with OpenTelemetry 🕰️

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB