Using SSH for deployment in Codeship Pro
Using SSH is a simple way to run commands on a remote server. For example, to update a Docker Swarm stack after a successful build.
Current solution
At the moment, loading build arguments from an encrypted file does not work correctly on Codeship.
Create a deployment service
This service will be responsible for connecting to the remote host. Create the following Dockerfile
for the deployment service:
FROM ubuntu:bionic# Other commands, e.g. copy docker-compose.yml
# ...RUN echo $'echo mkdir ~/.ssh \n\
echo ${DEPLOYMENT_SSH_PRIVATE_KEY_BASE64} | base64 -d > ~/.ssh/id_rsa \n\
chmod 600 ~/.ssh/id_rsa' > /tmp/get_ssh_id.shRUN chmod +x /tmp/get_ssh_id.sh
RUN apt-get update && apt-get install -y -qq sshCMD ["echo", "Image built!"]
This will create a script in /tmp
that will get the SSH private key from an environment variable and install it. Now create the Codeship service. Add this to your codeship-services.yml
:
# Image that will perform the deployment
# Needed env args: DEPLOYMENT_SSH_PRIVATE_KEY_BASE64
my-deployment-service:
build:
image: my-deployment-service
dockerfile: Dockerfile.deployment-service
encrypted_env_file: deployment-service.env_args.encrypted
Create an encrypted environment arguments file
Environment argument files do not support multiline string. An easy solution to this is to encode the SSH private key in base64 (this does not encrypt the key). Create deployment-service.env_args
:
DEPLOYMENT_SSH_PRIVATE_KEY_BASE64=long_base64_encoded_string
Encrypt it:
jet encrypt --key-path codeship.aes deployment-service.env_args deployment-service.env_args.encrypted
Don’t forget to delete the unencrypted file.
Using the service in Codeship steps
Now you can use SSH and SCP in your steps, with one extra step: run the /tmp/get_ssh_id.sh
script first. This means wrapping your commands in /bin/bash -c "/tmp/get_ssh_id.sh && your_command here"
. Example:
- name: copy_docker_compose_file
service: my-deployment-service
command: "/bin/bash -c \"/tmp/get_ssh_id.sh && /usr/bin/scp -oStrictHostKeyChecking=accept-new /tmp/docker-compose.prod.yml deploy@my.app:~/\""
tag: release
It’s not pretty, but it works.
How it should work: build arguments
This doesn’t work. The encrypted_build_args
directive does not work correctly: it doesn’t load the build arguments. When Codeship fixes this, this (more elegant) way will work.
Create a deployment service
This service will be responsible for connecting to the remote host. Create the following Dockerfile
for the deployment service:
FROM ubuntu:bionic
ARG SSH_PRIVATE_KEY_BASE64# Other commands, e.g. copy docker-compose.yml
# ...RUN mkdir ~/.ssh
RUN echo ${SSH_PRIVATE_KEY_BASE64} | base64 -d > ~/.ssh/id_rsa
RUN chmod 600 ~/.ssh/id_rsa
RUN apt-get update && apt-get install -y -qq ssh
CMD ["echo", "Image works"]
Now create the Codeship service. Add this to your codeship-services.yml
:
# Image that will perform the deployment
# Needed build args: SSH_PRIVATE_KEY_BASE64
my-deployment-service:
build:
image: my-api-deployment-service
dockerfile: Dockerfile.deployment-service
encrypted_build_args: deployment-service.build_args.encrypted
Create an encrypted build arguments file
Build argument files do not support multiline strings. An easy solution to this is to encode the SSH private key in base64 (this does not encrypt the key). Create deployment-service.build_args
:
SSH_PRIVATE_KEY_BASE64=long_base64_encoded_string
Encrypt it:
jet encrypt --key-path codeship.aes deployment-service.build_args deployment-service.build_args.encrypted
Don’t forget to delete the unencrypted file.
Using the service in Codeship steps
Now that we have the service, we can use ssh
and scp
in codeship-steps.yml
:
- name: copy_docker_compose_file
service: my-deployment-service
command: '/usr/bin/scp -oStrictHostKeyChecking=accept-new /tmp/docker-compose.prod.yml deploy@your.app:~/'
tag: release- name: pull_new_images
service: my-deployment-service
command: "/usr/bin/ssh -oStrictHostKeyChecking=accept-new deploy@your.app 'docker-compose -f docker-compose.prod.yml pull'"
tag: release- name: update_stack
service: my-deployment-service
command: "/usr/bin/ssh -oStrictHostKeyChecking=accept-new deploy@your.app 'docker stack deploy --compose-file docker-compose.prod.yml my-api --with-registry-auth'"
tag: release
-oStrictHostKeyChecking=accept-new
will auto-accept the server’s SSH key. You can add a known_hosts
file with the server’s public key.