TimDaub / hetzner-cloud-deploy-server-action

Deploy a Hetzner Cloud Server from a GitHub Action.
GNU General Public License v3.0
31 stars 14 forks source link
actions cloud continous github hetzner integration

Hetzner Cloud Deploy GitHub Action

Deploy a Hetzner Cloud Server from a GitHub Action.

Hetzner is a zero-carbon infrastructure provider.

Usage

See action.yml.

Basic:

jobs:
  build:
    runs-on: Ubuntu-20.04
    steps:
      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2
        with:
          server-name: "gh-actions-server"
          server-image: "ubuntu-20.04"
          server-type: "cx11"
          server-location: "nbg1"
          ssh-key-name: "my key name"
          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}
  1. Create a Hetzner Account
  2. Visit the Hetzner Cloud Console at console.hetzner.cloud, select your project, and create a new Read & Write API token ("Security" => "API Tokens").
  3. In the "Security" tab in the Hetzner Cloud Console, you can check your ssh key's name.
  4. Add the Hetzner API token to your GitHub repositories secrets ("Settings" => "Secrets") as HCLOUD_TOKEN.
  5. To know which server-images and server-types Hetzner provides, check the FAQ.

Notes

FAQ

How do I get all possible images to build from?

You can use the Hetzner Cloud API. The following curl command works well with jq:

$ curl \
  -H "Authorization: Bearer $API_TOKEN" \
  'https://api.hetzner.cloud/v1/images' | jq '.images[].name'

"ubuntu-16.04"
"debian-9"
"centos-7"
"ubuntu-18.04"
"debian-10"
"centos-8"
"ubuntu-20.04"
"fedora-32"
"fedora-33"

How do I get all possible server types?

You can use the Hetzner Cloud API. The following curl command works well with jq:

$ curl \
  -H "Authorization: Bearer $API_TOKEN" \
  'https://api.hetzner.cloud/v1/server_types' | jq '.server_types[].name'

"cx11"
"cx11-ceph"
"cx21"
"cx21-ceph"
"cx31"
"cx31-ceph"
"cx41"
"cx41-ceph"
"cx51"
"cx51-ceph"
"ccx11"
"ccx21"
"ccx31"
"ccx41"
"ccx51"
"cpx11"
"cpx21"
"cpx31"
"cpx41"
"cpx51"

How do I get all possible locations?

You can use the Hetzner Cloud API. The following curl command works well with jq:

$ curl \
  -H "Authorization: Bearer $API_TOKEN" \
  'https://api.hetzner.cloud/v1/locations' | jq '.locations[].name'

"fsn1"
"nbg1"
"hel1"
"ash"
"hil"

How do I get the ID of a Floating IP?

You can use the Hetzner Cloud API. The following curl command works well with jq:

$ curl -s \
  -H "Authorization: Bearer $API_TOKEN" \
  'https://api.hetzner.cloud/v1/floating_ips' | jq '.floating_ips[].id'
1
2
3

Why is this action useful?

In combination with webfactory/ssh-agent you can use it to provision a Hetzner Cloud instance completely from within a GitHub Action. An example:

jobs:
  build:
    runs-on: Ubuntu-20.04
    steps:
      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2
        with:
          server-name: "gh-actions-server"
          server-image: "ubuntu-20.04"
          server-type: "cx11"
          server-location: "nbg1"
          ssh-key-name: "my key name"
          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}
      - uses: webfactory/ssh-agent@v0.4.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
      - run: mkdir -p ~/.ssh/ && ssh-keyscan -H $SERVER_IPV4 >> ~/.ssh/known_hosts
      - run: ssh root@$SERVER_IPV4 touch tim_was_here
      - run: ssh root@$SERVER_IPV4 ls

After all steps have run, your provisioned Hetzner instance gets shutdown by the cleanup script.

How do I use this Action with e.g. a Domain Name?

All Hetzner servers created with this Action get assigned an arbitrary IP, which can make it difficult to immediately set it as an A-record on a domain you own.

Hetzner, however, provides a feature called Floating IPs, that allows a user to assign a static IP to a server.

By setting your DNS A-record to a floating IP and adding its ID as an input, you can hence make your launched server predictably-addressable.

Be aware that the floating IP needs to be in the same location as the server. To achieve this, use the additional parameter server-location.

Specifically, working with floating IPs can get a bit messy. Here's an example configuration.

jobs:
  build:
    runs-on: Ubuntu-20.04
    steps:
      - uses: TimDaub/hetzner-cloud-deploy-server-action@v2
        with:
          server-name: "server"
          server-image: "ubuntu-20.04"
          server-type: "cx11"
          server-location: "fsn1"
          ssh-key-name: "my key name"
          hcloud-token: ${{ secrets.HCLOUD_TOKEN }}
          startup-timeout: 40000
          floating-ip-id: my-id
          floating-ip-assignment-timeout: 30000
      - uses: webfactory/ssh-agent@v0.4.1
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
      - run: mkdir -p ~/.ssh/ && ssh-keyscan -H $SERVER_IPV4 >> ~/.ssh/known_hosts
      - run: ssh root@$SERVER_IPV4 "ip addr add $SERVER_FLOATING_IPV4 dev eth0"
      - run: mkdir -p ~/.ssh/ && ssh-keyscan -H $SERVER_FLOATING_IPV4 >> ~/.ssh/known_hosts
      - run: ssh root@$SERVER_FLOATING_IPV4 touch tim_was_here
      - run: ssh root@$SERVER_FLOATING_IPV4 ls

Note how we use ssh-keyscan twice here to configure the GitHub Action server for once with the SERVER_IPV4 but then later also with the SERVER_FLOATING_IPV4. This specific step is optional, but it allows a subsequent step to directly connect to the floating IP.

Can I lose money when running this script?

Yes, you certainly can.

There may be instances where something within my script's cleanup fails and the instance remains online. So if you're planning to run your tests many times or if you're planning to launch huge instances, please make sure to double check if some instances remain running after the action has completed.

Also, please note that Hetzner bills per hours, not minutes. This means that a 30 second run will be billed as a 1 hour run, a 61 minute run will be billed as a 2 hour run, etc. (#14)

If you push very frequently, you might want to ensure that your workflow requires explicit approval before running by using GitHub's Environment approval mechanism.

You have been warned.

What do these errors mean?

License

See License.